diff --git a/drivers/pci/CMakeLists.txt b/drivers/pci/CMakeLists.txt index 0627376917..425fcfc3a0 100644 --- a/drivers/pci/CMakeLists.txt +++ b/drivers/pci/CMakeLists.txt @@ -41,3 +41,11 @@ if(CONFIG_PCI) target_sources(drivers PRIVATE ${SRCS}) endif() # CONFIG_PCI + +if(CONFIG_PCI_ENDPOINT) + + set(SRCS pci_epc.c pci_epc_mem.c pci_epf.c) + + target_sources(drivers PRIVATE ${SRCS}) + +endif() # CONFIG_PCI_ENDPOINT diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index f85217c7a5..38f6b0a840 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -74,3 +74,12 @@ config PCI_UIO_IVSHMEM_NPOLLWAITERS depends on PCI_UIO_IVSHMEM endif # PCI + +menuconfig PCI_ENDPOINT + bool "PCI Endpoint Support" + default n + ---help--- + Enable this configuration option to support configurable PCI + endpoint. This should be enabled if the platform has a PCI + controller that can operate in endpoint mode. + diff --git a/drivers/pci/Make.defs b/drivers/pci/Make.defs index 58ffe28de4..3afde85c8f 100644 --- a/drivers/pci/Make.defs +++ b/drivers/pci/Make.defs @@ -37,6 +37,10 @@ ifeq ($(CONFIG_PCI_UIO_IVSHMEM),y) CSRCS += pci_uio_ivshmem.c endif +ifeq ($(CONFIG_PCI_ENDPOINT),y) +CSRCS += pci_epc.c pci_epc_mem.c pci_epf.c +endif + # Include PCI device driver build support DEPPATH += --dep-path pci diff --git a/drivers/pci/pci_epc.c b/drivers/pci/pci_epc.c new file mode 100644 index 0000000000..f395ba2f8f --- /dev/null +++ b/drivers/pci/pci_epc.c @@ -0,0 +1,1011 @@ +/**************************************************************************** + * drivers/pci/pci_epc.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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static mutex_t g_pci_epc_lock = NXMUTEX_INITIALIZER; +static struct list_node g_pci_epc_device_list = + LIST_INITIAL_VALUE(g_pci_epc_device_list); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_get_epc + * + * Description: + * This function is used to get a PCI endpoint controller. + * + * Invoke to get struct pci_epc_ctrl_s * corresponding to the device name + * of the endpoint controller. + * + * Input Parameters: + * epc_name - Device name of the endpoint controller + * + * Returned Value: + * Return epc created if success, NULL if failed + ****************************************************************************/ + +FAR struct pci_epc_ctrl_s *pci_get_epc(FAR const char *epc_name) +{ + FAR struct pci_epc_ctrl_s *res = NULL; + FAR struct pci_epc_ctrl_s *epc; + int ret; + + DEBUGASSERT(epc_name != NULL); + + ret = nxmutex_lock(&g_pci_epc_lock); + if (ret < 0) + { + return NULL; + } + + list_for_every_entry(&g_pci_epc_device_list, epc, struct pci_epc_ctrl_s, + node) + { + if (strcmp(epc_name, epc->name) == 0) + { + res = epc; + break; + } + } + + nxmutex_unlock(&g_pci_epc_lock); + return res; +} + +/**************************************************************************** + * Name: pci_epc_get_next_free_bar + * + * Description: + * Helper to get unreserved BAR starting from bar. + * + * Invoke to get the next unreserved BAR starting from barno that can be + * used for endpoint function. For any incorrect value in bar_reserved return + * '0'. + * + * Input Parameters: + * epc_features - pci_epc_features_s structure that holds the reserved bar + * bitmap + * bar - The starting BAR number from where unreserved BAR should + * be searched + * + * Returned Value: + * Return the member if success, negative if failed + ****************************************************************************/ + +int pci_epc_get_next_free_bar( + FAR const struct pci_epc_features_s *epc_features, int barno) +{ + unsigned long free_bar; + + if (epc_features == NULL) + { + return -EINVAL; + } + + /* If 'bar - 1' is a 64-bit BAR, move to the next BAR */ + + if ((epc_features->bar_fixed_64bit << 1) & (1 << barno)) + { + barno++; + } + + /* Find if the reserved BAR is also a 64-bit BAR */ + + free_bar = epc_features->bar_reserved & epc_features->bar_fixed_64bit; + + /* Set the adjacent bit if the reserved BAR is also a 64-bit BAR */ + + free_bar <<= 1; + free_bar |= epc_features->bar_reserved; + + free_bar = find_next_zero_bit(&free_bar, PCI_STD_NUM_BARS, barno); + if (free_bar >= PCI_STD_NUM_BARS) + { + return -ENOENT; + } + + return free_bar; +} + +/**************************************************************************** + * Name: pci_epc_get_features + * + * Description: + * This function is used to get the features supported by EPC. + * + * Invoke to get the features provided by the EPC which may be + * specific to an endpoint function. Returns pci_epc_features_s on success + * and NULL for any failures. + * + * Input Parameters: + * epc - The features supported by *this* EPC device will be returned + * funcno - The features supported by the EPC device specific to the + * endpoint function with funcno will be returned + * + * Returned Value: + * Epc features if success, NULL if failed + ****************************************************************************/ + +FAR const struct pci_epc_features_s * +pci_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno) +{ + FAR const struct pci_epc_features_s *epc_features; + + if (epc == NULL || epc->ops->get_features == NULL || + funcno >= epc->max_functions) + { + return NULL; + } + + nxmutex_lock(&epc->lock); + epc_features = epc->ops->get_features(epc, funcno); + nxmutex_unlock(&epc->lock); + + return epc_features; +} + +/**************************************************************************** + * Name: pci_epc_stop + * + * Description: + * This function is used to stop the PCI link. + * + * Invoke to stop the PCI link. + * + * Input Parameters: + * epc - The link of the EPC device that has to be stopped + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_stop(FAR struct pci_epc_ctrl_s *epc) +{ + if (epc == NULL || !epc->ops->stop) + { + return; + } + + nxmutex_lock(&epc->lock); + epc->ops->stop(epc); + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_start + * + * Description: + * This function is used to start the PCI link. + * + * Invoke to start the PCI link. + * + * Input Parameters: + * epc - The link of *this* EPC device has to be started + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_start(FAR struct pci_epc_ctrl_s *epc) +{ + int ret; + + if (epc == NULL || !epc->ops->start) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->start(epc); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_raise_irq + * + * Description: + * This function is used to interrupt the host system. + * + * Invoke to raise an legacy, MSI or MSI-X interrupt. + * + * Input Parameters: + * epc - The EPC device which has to interrupt the host + * funcno - The physical endpoint function number in the EPC device + * type - Specify the type of interrupt; legacy, MSI or MSI-X + * interrupt_num - The MSI or MSI-X interrupt number with range (1-N) + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + enum pci_epc_irq_type_e type, uint16_t interrupt_num) +{ + int ret; + + if (epc == NULL || epc->ops->raise_irq == NULL || + funcno >= epc->max_functions) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->raise_irq(epc, funcno, type, interrupt_num); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_map_msi_irq + * + * Description: + * Map physical address to MSI address and return MSI data. + * + * Invoke to map physical address to MSI address and return MSI data. The + * physical address should be an address in the outbound region. This is + * required to implement doorbell functionality of NTB wherein EPC on either + * side of the interface (primary and secondary) can directly write to the + * physical address (in outbound region) of the other interface to ring + * doorbell. + * + * Input Parameters: + * epc - The EPC device which has to interrupt the host + * funcno - The physical endpoint function number in the EPC + * device + * phys_addr - The physical address of the outbound region + * interrupt_num - The MSI or MSI-X interrupt number with range (1-N) + * entry_size - Size of Outbound address region for each interrupt + * msi_data - The data that should be written in order to raise MSI + * interrupt with interrupt number as 'interrupt num' + * msi_addr_offset - Offset of MSI address from the aligned outbound + * address to which the MSI address is mapped + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_map_msi_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr, uint8_t interrupt_num, + uint32_t entry_size, FAR uint32_t *msi_data, + FAR uint32_t *msi_addr_offset) +{ + int ret; + + if (epc == NULL && epc->ops->map_msi_irq == NULL) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->map_msi_irq(epc, funcno, phys_addr, interrupt_num, + entry_size, msi_data, msi_addr_offset); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_get_msi + * + * Description: + * Get the number of MSI interrupt numbers allocated. + * + * Message Control Register for MSI:bit6-bit4:Multiple Message Enable + * Software writes to this field to indicate the number of allocate vectors + * Equal to or less than the number of requested vectors. The number of + * allocated vectors is aligned to a power of two. If a Function requests + * four vectors (indicated by a Multiple Message Capable encoding of + * 010b), system software can allocate either four, two, or one vector + * by writing a 010b, 001b, or 000b to this field, respectively. When + * MSI Enable is Set, a Function will be allocated at least 1 vector + * + * Invoke to get the number of MSI interrupts allocated by the RC. + * + * Input Parameters: + * epc - The EPC device to which MSI interrupts was requested + * funcno - The physical endpoint function number in the EPC device + * + * Returned Value: + * Return interrupt number if success, negative if failed + ****************************************************************************/ + +int pci_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno) +{ + int interrupt; + + if (epc == NULL || funcno >= epc->max_functions || + epc->ops->get_msi == NULL) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + interrupt = epc->ops->get_msi(epc, funcno); + nxmutex_unlock(&epc->lock); + + if (interrupt >= 0) + { + interrupt = 1 << interrupt; + } + + return interrupt; +} + +/**************************************************************************** + * Name: pci_epc_set_msi + * + * Description: + * Set the number of MSI interrupt numbers required. + * + * Message Control Register for MSI:bit6-bit4:Multiple Message Enable + * Software writes to this field to indicate the number of allocate vectors + * Equal to or less than the number of requested vectors. The number of + * allocated vectors is aligned to a power of two. If a Function requests + * four vectors (indicated by a Multiple Message Capable encoding of + * 010b), system software can allocate either four, two, or one vector + * by writing a 010b, 001b, or 000b to this field, respectively. When + * MSI Enable is Set, a Function will be allocated at least 1 vector + * + * Invoke to set the required number of MSI interrupts. + * + * Input Parameters: + * epc - The EPC device on which MSI has to be configured + * funcno - The physical endpoint function number in the EPC device + * interrupts - Number of MSI interrupts required by the EPF + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uint8_t interrupts) +{ + int ret; + + if (epc == NULL || funcno >= epc->max_functions || + interrupts < 1 || interrupts > 32 || epc->ops->set_msi == NULL) + { + return -EINVAL; + } + + interrupts = order_base_2(interrupts); + + nxmutex_lock(&epc->lock); + ret = epc->ops->set_msi(epc, funcno, interrupts); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_get_msix + * + * Description: + * Get the number of MSI-X interrupt numbers allocated. + * + * Message Control Register for MSI-X:bit10-bit0:Table Size + * System software reads this field to determine the MSI-X Table Size N, + * which is encoded as N-1. For example, a returned value of 000 0000 0011b + * indicates a table size of 4. + * + * Invoke to get the number of MSI-X interrupts allocated by the RC. + * + * Input Parameters: + * epc - The EPC device to which MSI-X interrupts was requested + * funcno - The physical endpoint function number in the EPC device + * + * Returned Value: + * Return interrupt + 1 number if success, negative if failed + ****************************************************************************/ + +int pci_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno) +{ + int interrupt; + + if (epc == NULL || funcno >= epc->max_functions || + epc->ops->get_msix == NULL) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + interrupt = epc->ops->get_msix(epc, funcno); + nxmutex_unlock(&epc->lock); + + if (interrupt >= 0) + { + interrupt += 1; + } + + return interrupt; +} + +/**************************************************************************** + * Name: pci_epc_set_msix + * + * Description: + * Set the number of MSI-X interrupt numbers required. + * + * Message Control Register for MSI-X:bit10-bit0:Table Size + * System software reads this field to determine the MSI-X Table Size N, + * which is encoded as N-1. For example, a returned value of 000 0000 0011b + * indicates a table size of 4. + * + * Invoke to set the required number of MSI-X interrupts. + * + * Input Parameters: + * epc - The EPC device on which MSI-X has to be configured + * funcno - The physical endpoint function number in the EPC device + * interrupts - Number of MSI-X interrupts required by the EPF + * barno - BAR where the MSI-X table resides + * offset - Offset pointing to the start of MSI-X table + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uint16_t interrupts, int barno, uint32_t offset) +{ + int ret; + + if (epc == NULL || funcno >= epc->max_functions || interrupts < 1 || + interrupts > 2048 || epc->ops->set_msix == NULL) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->set_msix(epc, funcno, interrupts - 1, barno, offset); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_unmap_addr + * + * Description: + * Unmap CPU address from PCI address. + * + * Invoke to unmap the CPU address from PCI address. + * + * Input Parameters: + * epc - The EPC device on which address is allocated + * funcno - The physical endpoint function number in the EPC device + * phys_addr - Physical address of the local systeme + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr) +{ + if (epc == NULL || funcno >= epc->max_functions || + epc->ops->unmap_addr == NULL) + { + return; + } + + nxmutex_lock(&epc->lock); + epc->ops->unmap_addr(epc, funcno, phys_addr); + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_map_addr + * + * Description: + * Map CPU address to PCI address. + * + * Invoke to map CPU address with PCI address. + * + * Input Parameters: + * epc - The EPC device on which address is allocated + * funcno - The physical endpoint function number in the EPC device + * phys_addr - Physical address of the local system + * pci_addr - PCI address to which the physical address should be mapped + * size - The size of the allocation + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr, uint64_t pci_addr, size_t size) +{ + int ret; + + if (epc == NULL || funcno >= epc->max_functions || + epc->ops->map_addr == NULL) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->map_addr(epc, funcno, phys_addr, pci_addr, size); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_clear_bar + * + * Description: + * Reset the BAR. + * + * Invoke to reset the BAR of the endpoint device. + * + * Input Parameters: + * epc - The EPC device for which the BAR has to be cleared + * funcno - The physical endpoint function number in the EPC device + * bar - The struct bar that contains the BAR information + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_bar_s *bar) +{ + if (epc == NULL || funcno >= epc->max_functions || + epc->ops->clear_bar == NULL || + (bar->barno == PCI_STD_NUM_BARS - 1 && + (bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64))) + { + return; + } + + nxmutex_lock(&epc->lock); + epc->ops->clear_bar(epc, funcno, bar); + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_set_bar + * + * Description: + * Configure BAR in order for host to assign PCI addr space. + * + * Invoke to configure the BAR of the endpoint device. + * + * Input Parameters: + * epc - The EPC device on which BAR has to be configured + * funcno - The physical endpoint function number in the EPC device + * bar - The struct bar that contains the BAR information + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_bar_s *bar) +{ + int flags = bar->flags; + int ret; + + if (epc == NULL || funcno >= epc->max_functions || + (bar->barno == PCI_STD_NUM_BARS - 1 && + flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || + (bar->size > UINT32_MAX && + !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) || + epc->ops->set_bar == NULL) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->set_bar(epc, funcno, bar); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_write_header + * + * Description: + * Write standard configuration header. + * + * Invoke to write the configuration header to the endpoint controller. + * Every endpoint controller will have a dedicated location to which the + * standard configuration header would be written. The callback function + * should write the header fields to this dedicated location. + * + * Input Parameters: + * epc - The EPC device to which the configuration header should be + * written + * funcno - The physical endpoint function number in the EPC device + * header - Standard configuration header fields + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_write_header(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_header_s *header) +{ + int ret; + + if (epc == NULL || epc->ops->write_header == NULL || + funcno >= epc->max_functions) + { + return -EINVAL; + } + + nxmutex_lock(&epc->lock); + ret = epc->ops->write_header(epc, funcno, header); + nxmutex_unlock(&epc->lock); + + return ret; +} + +/**************************************************************************** + * Name: pci_epc_add_epf + * + * Description: + * This function is used to bind PCI endpoint function to an endpoint + * controller. + * + * A PCI endpoint device can have one or more functions In the case of + * PCIe,the specification allows up to 8 PCIe endpoint functions Invoke + * pci_epc_add_epf() to add a PCI endpoint function to an endpoint + * controller. + * + * Input Parameters: + * epc - The EPC device to which the endpoint function should be added + * epf - The endpoint function to be added + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_add_epf(FAR struct pci_epc_ctrl_s *epc, + FAR struct pci_epf_device_s *epf) +{ + uint32_t funcno; + int ret = 0; + + DEBUGASSERT(epc != NULL && epf != NULL); + + if (epf->epc) + { + return -EBUSY; + } + + nxmutex_lock(&epc->lock); + + funcno = find_first_zero_bit(&epc->funcno_map, epc->max_functions); + if (funcno >= epc->max_functions) + { + pcierr("Exceeding max supported Function Number\n"); + ret = -ENOENT; + goto out; + } + + set_bit(funcno, &epc->funcno_map); + epf->funcno = funcno; + epf->epc = epc; + + list_add_tail(&epc->epf, &epf->node); + +out: + nxmutex_unlock(&epc->lock); + return ret; +} + +/**************************************************************************** + * Name: pci_epc_remove_epf + * + * Description: + * This function is used to remove PCI endpoint function from endpoint + * controller. + * + * Invoke to remove PCI endpoint function from the endpoint controller. + * + * Input Parameters: + * epc - The EPC device from which the endpoint function should be removed + * epf - The endpoint function to be removed + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_remove_epf(FAR struct pci_epc_ctrl_s *epc, + FAR struct pci_epf_device_s *epf) +{ + if (epc == NULL || epf == NULL) + { + return; + } + + nxmutex_lock(&epc->lock); + clear_bit(epf->funcno, &epc->funcno_map); + list_delete(&epf->node); + epf->epc = NULL; + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_linkup + * + * Description: + * Notify the EPF device that EPC device has established a connection with + * the Root Complex. + * + * Invoke to Notify the EPF device that the EPC device has established a + * connection with the Root Complex. + * + * Input Parameters: + * epc - The EPC device which has established link with the host + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_linkup(FAR struct pci_epc_ctrl_s *epc) +{ + FAR struct pci_epf_device_s *epf; + + if (epc == NULL) + { + return; + } + + nxmutex_lock(&epc->lock); + list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node) + { + nxmutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->link_up) + { + epf->event_ops->link_up(epf); + } + + nxmutex_unlock(&epf->lock); + } + + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_linkdown + * + * Description: + * Notify the EPF device that EPC device has dropped the connection with + * the Root Complex. + * + * Invoke to Notify the EPF device that the EPC device has dropped the + * connection with the Root Complex. + * + * Input Parameters: + * epc - The EPC device which has dropped the link with the host + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_linkdown(FAR struct pci_epc_ctrl_s *epc) +{ + struct pci_epf_device_s *epf; + + if (epc == NULL) + { + return; + } + + nxmutex_lock(&epc->lock); + list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node) + { + nxmutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->link_down) + { + epf->event_ops->link_down(epf); + } + + nxmutex_unlock(&epf->lock); + } + + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_init_notify + * + * Description: + * Notify the EPF device that EPC device's core initialization is + * completed. + * + * Invoke to Notify the EPF device that the EPC device's initialization + * is completed. + * + * Input Parameters: + * epc - The EPC device whose core initialization is completed + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_init_notify(FAR struct pci_epc_ctrl_s *epc) +{ + FAR struct pci_epf_device_s *epf; + + if (epc == NULL) + { + return; + } + + nxmutex_lock(&epc->lock); + list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node) + { + nxmutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->core_init) + { + epf->event_ops->core_init(epf); + } + + nxmutex_unlock(&epf->lock); + } + + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_bme_notify + * + * Description: + * Notify the EPF device that the EPC device has received the BME event + * from the Root complex. + * + * Invoke to Notify the EPF device that the EPC device has received the Bus + * Master Enable (BME) event from the Root complex. + * + * Input Parameters: + * epc - The EPC device that received the BME event + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_bme_notify(FAR struct pci_epc_ctrl_s *epc) +{ + FAR struct pci_epf_device_s *epf; + + if (epc == NULL) + { + return; + } + + nxmutex_lock(&epc->lock); + list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node) + { + nxmutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->bme) + { + epf->event_ops->bme(epf); + } + + nxmutex_unlock(&epf->lock); + } + + nxmutex_unlock(&epc->lock); +} + +/**************************************************************************** + * Name: pci_epc_create + * + * Description: + * This function is used to destroy the EPC device. + * + * Invoke to create a new EPC device and add it to pci_epc class. + * + * Input Parameters: + * name - EPC name strings + * ops - Function pointers for performing EPC operations + * Returned Value: + * Return struct pci_epc_ctrl_s * if success, NULL if failed. + ****************************************************************************/ + +FAR struct pci_epc_ctrl_s * +pci_epc_create(FAR const char *name, FAR const struct pci_epc_ops_s *ops) +{ + FAR struct pci_epc_ctrl_s *epc; + size_t len; + + if (name == NULL || ops == NULL) + { + return NULL; + } + + len = strlen(name) + 1; + epc = kmm_zalloc(sizeof(*epc) + len); + if (epc == NULL) + { + return NULL; + } + + memcpy(epc->name, name, len); + nxmutex_init(&epc->lock); + list_initialize(&epc->epf); + epc->ops = ops; + + nxmutex_lock(&g_pci_epc_lock); + list_add_tail(&g_pci_epc_device_list, &epc->node); + nxmutex_unlock(&g_pci_epc_lock); + + return epc; +} + +/**************************************************************************** + * Name: pci_epc_destroy + * + * Description: + * This function is used to create a new endpoint controller (EPC) device. + * + * Invoke to destroy the PCI EPC device. + * + * Input Parameters: + * epc - The EPC device that has to be destroyed + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_destroy(FAR struct pci_epc_ctrl_s *epc) +{ + if (epc == NULL) + { + return; + } + + nxmutex_lock(&g_pci_epc_lock); + list_delete(&epc->node); + nxmutex_unlock(&g_pci_epc_lock); + + nxmutex_destroy(&epc->lock); + kmm_free(epc); +} diff --git a/drivers/pci/pci_epc_mem.c b/drivers/pci/pci_epc_mem.c new file mode 100644 index 0000000000..d33aeec622 --- /dev/null +++ b/drivers/pci/pci_epc_mem.c @@ -0,0 +1,289 @@ +/**************************************************************************** + * 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); +} + diff --git a/drivers/pci/pci_epf.c b/drivers/pci/pci_epf.c new file mode 100644 index 0000000000..3056030c1b --- /dev/null +++ b/drivers/pci/pci_epf.c @@ -0,0 +1,522 @@ +/**************************************************************************** + * drivers/pci/pci_epf.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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef ALIGN_UP +# define ALIGN_UP(s, a) (((s) + (a) - 1) & ~((a) - 1)) +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct list_node g_pci_epf_device_list = + LIST_INITIAL_VALUE(g_pci_epf_device_list); +static struct list_node g_pci_epf_driver_list = + LIST_INITIAL_VALUE(g_pci_epf_driver_list); +static mutex_t g_pci_epf_lock = NXMUTEX_INITIALIZER; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_epf_match_id + ****************************************************************************/ + +static FAR const struct pci_epf_device_id_s * +pci_epf_match_id(FAR const struct pci_epf_device_s *dev, + FAR const struct pci_epf_device_id_s *id) +{ + while (id->name[0]) + { + if (strcmp(dev->name, id->name) == 0) + { + return id; + } + + id++; + } + + return NULL; +} + +/**************************************************************************** + * Name: pci_epf_match_device + ****************************************************************************/ + +static FAR const struct pci_epf_device_id_s * +pci_epf_match_device(FAR struct pci_epf_device_s *dev, + FAR struct pci_epf_driver_s *drv) +{ + if (drv->id_table) + { + return pci_epf_match_id(dev, drv->id_table); + } + else + { + return NULL; + } +} + +/**************************************************************************** + * Name: pci_epf_unbind + * + * Description: + * Notify the function driver that the binding between the EPF device and + * EPC device has been lost. + * + * Invoke to notify the function driver that the binding between the EPF + * device and EPC device has been lost. + * + * Input Parameters: + * epf - The EPF device which has lost the binding with the EPC device + * + * Returned Value: + * None + ****************************************************************************/ + +static void pci_epf_unbind(FAR struct pci_epf_device_s *epf) +{ + if (epf->driver == NULL || epf->driver->ops->unbind == NULL) + { + pcierr("Invalid epf parameter, unbind failed!\n"); + return; + } + + nxmutex_lock(&epf->lock); + if (epf->is_bound) + { + epf->driver->ops->unbind(epf); + epf->is_bound = false; + } + + nxmutex_unlock(&epf->lock); +} + +/**************************************************************************** + * Name: pci_epf_bind + * + * Description: + * Notify the function driver that the EPF device has been bound to a EPC + * device. + * + * Invoke to notify the function driver that it has been bound to a EPC + * device. + * + * Input Parameters: + * epf - The EPF device which has been bound to the EPC device + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +static int pci_epf_bind(FAR struct pci_epf_device_s *epf) +{ + int ret; + + if (epf->driver == NULL || epf->driver->ops->bind == NULL) + { + pcierr("Invalid epf parameter, bind failed!\n"); + return -EINVAL; + } + + nxmutex_lock(&epf->lock); + ret = epf->driver->ops->bind(epf); + if (ret >= 0) + { + epf->is_bound = true; + } + + nxmutex_unlock(&epf->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_epf_free_space + * + * Description: + * Free the allocated PCI EPF register space + * + * Invoke to free the allocated PCI EPF register space. + * + * Input Parameters: + * epf - The EPF device from whom to free the memory + * barno - The BAR number corresponding to the register space + * addr - The virtual address of the PCI EPF register space + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epf_free_space(FAR struct pci_epf_device_s *epf, + int barno, FAR void *addr) +{ + FAR struct pci_epf_bar_s *bar; + + if (addr == NULL || barno >= PCI_STD_NUM_BARS) + { + return; + } + + bar = epf->bar; + + kmm_free(addr); + + bar[barno].phys_addr = 0; + bar[barno].addr = NULL; + bar[barno].size = 0; + bar[barno].barno = 0; + bar[barno].flags = 0; +} + +/**************************************************************************** + * Name: pci_epf_alloc_space + * + * Description: + * This function is used to allocate memory for the PCI EPF register + * space. + * + * Invoke to allocate memory for the PCI EPF register space. + * + * Input Parameters: + * epf - The EPF device to whom allocate the memory + * barno - The BAR number corresponding to the allocated register space + * size - The size of the memory that has to be allocated + * align - Alignment size for the allocation region + * + * Returned Value: + * Return space address malloced if success, otherwise NULL + ****************************************************************************/ + +FAR void *pci_epf_alloc_space(FAR struct pci_epf_device_s *epf, int barno, + size_t size, size_t align) +{ + FAR struct pci_epf_bar_s *bar; + FAR void *space; + uintptr_t phys_addr; + + if (epf == NULL || barno >= PCI_STD_NUM_BARS) + { + return NULL; + } + + if (size < 128) + { + size = 128; + } + + if (align) + { + size = ALIGN_UP(size, align); + } + else + { + size = roundup_pow_of_two(size); + } + + bar = epf->bar; + + space = kmm_zalloc(size); + if (space == NULL) + { + pcierr("Failed to allocate mem space\n"); + return NULL; + } + + phys_addr = up_addrenv_va_to_pa(space); + + bar[barno].phys_addr = phys_addr; + bar[barno].addr = space; + bar[barno].size = size; + bar[barno].barno = barno; + bar[barno].flags |= (size > UINT32_MAX) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; + + return space; +} + +/**************************************************************************** + * Name: pci_epf_device_register + * + * Description: + * This function is used to create a new PCI EPF device. + * + * Invoke to create a new PCI EPF device by providing the name of the + * function device. + * + * Input Parameters: + * epf - The name of the PCI EPF device be used to bind the EPF device to + * a EPF driver + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_device_register(FAR struct pci_epf_device_s *epf) +{ + FAR struct pci_epf_driver_s *drv; + FAR struct pci_epc_ctrl_s *epc; + int ret; + + DEBUGASSERT(epf != NULL || epf->name != NULL); + + ret = nxmutex_lock(&g_pci_epf_lock); + if (ret < 0) + { + return ret; + } + + list_add_tail(&g_pci_epf_device_list, &epf->node); + + list_for_every_entry(&g_pci_epf_driver_list, drv, + struct pci_epf_driver_s, node) + { + epf->id = pci_epf_match_device(epf, drv); + if (epf->id == NULL) + { + continue; + } + + /* Find the specify EPC by epc_name in epf structure */ + + epc = pci_get_epc(epf->epc_name); + if (epc == NULL) + { + ret = -ENODEV; + break; + } + + ret = drv->probe(epf); + if (ret < 0) + { + continue; + } + + epf->driver = drv; + + /* Added the epc to epf */ + + pci_epc_add_epf(epc, epf); + + /* Notify the function driver */ + + pci_epf_bind(epf); + break; + } + + nxmutex_unlock(&g_pci_epf_lock); + return ret; +} + +/**************************************************************************** + * Name: pci_epf_device_unregister + * + * Description: + * This function is used to unregister a PCI EPF device. + * + * Invoke to unregister the PCI EPF device. + * + * Input Parameters: + * epf - The PCI EPF driver that has to be unregistered device + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_device_unregister(FAR struct pci_epf_device_s *epf) +{ + FAR struct pci_epc_ctrl_s *epc; + int ret; + + ret = nxmutex_lock(&g_pci_epf_lock); + if (ret < 0) + { + return ret; + } + + pci_epf_unbind(epf); + epc = pci_get_epc(epf->epc_name); + if (epc) + { + pci_epc_remove_epf(epc, epf); + } + + if (epf->driver && epf->driver->remove) + { + epf->driver->remove(epf); + } + + epf->driver = NULL; + list_delete(&epf->node); + + nxmutex_unlock(&g_pci_epf_lock); + return ret; +} + +/**************************************************************************** + * Name: pci_epf_register_driver + * + * Description: + * This function is used to register a new PCI EPF driver. + * + * Input Parameters: + * drv - EPF driver + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_register_driver(FAR struct pci_epf_driver_s *drv) +{ + FAR struct pci_epf_device_s *epf; + FAR struct pci_epc_ctrl_s *epc; + int ret; + + if (drv == NULL || drv->ops == NULL || + drv->ops->bind == NULL || drv->id_table == NULL) + { + return -EINVAL; + } + + ret = nxmutex_lock(&g_pci_epf_lock); + if (ret < 0) + { + return ret; + } + + /* Add the driver to the pci epf driver list */ + + list_add_tail(&g_pci_epf_driver_list, &drv->node); + + list_for_every_entry(&g_pci_epf_device_list, epf, struct pci_epf_device_s, + node) + { + if (epf->driver != NULL) + { + continue; + } + + /* 1. Match epf device and epf driver. + * 2. Get the epc by name. + * 3. Add epc into epf. + * 4. Notify the epf driver be bond. + */ + + epf->id = pci_epf_match_device(epf, drv); + if (epf->id == NULL) + { + continue; + } + + epc = pci_get_epc(epf->name); + if (epc == NULL) + { + ret = -ENODEV; + break; + } + + ret = drv->probe(epf); + if (ret < 0) + { + continue; + } + + epf->driver = drv; + + /* Added the epc to epf */ + + pci_epc_add_epf(epc, epf); + + /* Notify the function driver */ + + pci_epf_bind(epf); + } + + nxmutex_unlock(&g_pci_epf_lock); + return ret; +} + +/**************************************************************************** + * Name: pci_epf_unregister_driver + * + * Description: + * This function is used to unregister the PCI EPF driver. + * + * Invoke to unregister the PCI EPF driver. + * + * Input Parameters: + * drv - The PCI EPF driver that has to be unregistered + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_unregister_driver(FAR struct pci_epf_driver_s *drv) +{ + FAR struct pci_epf_device_s *epf; + int ret; + + DEBUGASSERT(drv != NULL || drv->remove != NULL); + + ret = nxmutex_lock(&g_pci_epf_lock); + if (ret < 0) + { + return ret; + } + + list_for_every_entry(&g_pci_epf_device_list, epf, struct pci_epf_device_s, + node) + { + if (epf->driver == drv) + { + pci_epf_unbind(epf); + drv->remove(epf); + epf->driver = NULL; + } + } + + list_delete(&drv->node); + + nxmutex_unlock(&g_pci_epf_lock); + return ret; +} diff --git a/include/nuttx/pci/pci_epc.h b/include/nuttx/pci/pci_epc.h new file mode 100644 index 0000000000..755914df2f --- /dev/null +++ b/include/nuttx/pci/pci_epc.h @@ -0,0 +1,813 @@ +/**************************************************************************** + * include/nuttx/pci/pci_epc.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_PCI_PCI_EPC_H +#define __INCLUDE_NUTTX_PCI_PCI_EPC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum pci_epc_irq_type_e +{ + PCI_EPC_IRQ_UNKNOWN, + PCI_EPC_IRQ_LEGACY, + PCI_EPC_IRQ_MSI, + PCI_EPC_IRQ_MSIX, +}; + +/* struct pci_epc_features_s - Features supported by a EPC device per + * function + * + * linkup_notifier: Indicate if the EPC device can notify EPF driver on link + * up + * core_init_notifier: Indicate cores that can notify about their + * vailability for initialization + * msi_capable: Indicate if the endpoint function has MSI capability + * msix_capable: Indicate if the endpoint function has MSI-X capability + * bar_reserved: Bitmap to indicate reserved BAR unavailable to function + * driver + * bar_fixed_64bit: Bitmap to indicate fixed 64bit BARs + * bar_fixed_size: Array specifying the size supported by each BAR + * align: Alignment size required for BAR buffer allocation + */ + +struct pci_epc_features_s +{ + unsigned int linkup_notifier : 1; + unsigned int core_init_notifier : 1; + unsigned int msi_capable : 1; + unsigned int msix_capable : 1; + uint8_t bar_reserved; + uint8_t bar_fixed_64bit; + uint64_t bar_fixed_size[PCI_STD_NUM_BARS]; + size_t align; +}; + +/* struct pci_epc_ops_s - Set of function pointers for performing EPC + * operations + * + * write_header: Ops to populate configuration space header + * set_bar: Ops to configure the BAR + * clear_bar: Ops to reset the BAR + * map_addr: Ops to map CPU address to PCI address + * unmap_addr: Ops to unmap CPU address and PCI address + * set_msi: Ops to set the requested number of MSI interrupts in the MSI + * capability register + * get_msi: Ops to get the number of MSI interrupts allocated by the RC from + * the MSI capability register + * set_msix: Ops to set the requested number of MSI-X interrupts in the + * MSI-X capability register + * get_msix: Ops to get the number of MSI-X interrupts allocated by the RC + * from the MSI-X capability register + * raise_irq: Ops to raise a legacy, MSI or MSI-X interrupt + * map_msi_irq: Ops to map physical address to MSI address and return MSI + * data + * start: Ops to start the PCI link + * stop: Ops to stop the PCI link + * get_features: Ops to get the features supported by the EPC + * dma_xfer: Ops to transfer data between mapped Host memory and device + * memory with "System DMA" + */ + +struct pci_epc_ops_s +{ + CODE int (*write_header)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_header_s *hdr); + CODE int (*set_bar)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_bar_s *bar); + CODE void (*clear_bar)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_bar_s *bar); + CODE int (*map_addr)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t addr, uint64_t pci_addr, size_t size); + CODE void (*unmap_addr)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t addr); + CODE int (*set_msi)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uint8_t interrupts); + CODE int (*get_msi)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno); + CODE int (*set_msix)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uint16_t interrupts, int barno, uint32_t offset); + CODE int (*get_msix)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno); + CODE int (*raise_irq)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + enum pci_epc_irq_type_e type, + uint16_t interrupt_num); + CODE int (*map_msi_irq)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr, uint8_t interrupt_num, + uint32_t entry_size, FAR uint32_t *msi_data, + FAR uint32_t *msi_addr_offset); + CODE int (*start)(FAR struct pci_epc_ctrl_s *epc); + CODE void (*stop)(FAR struct pci_epc_ctrl_s *epc); + CODE FAR const struct pci_epc_features_s * + (*get_features)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno); + CODE int (*dma_xfer)(FAR struct pci_epc_ctrl_s *epc, uintptr_t mapped_addr, + uintptr_t local_addr, uint32_t size, bool fromdev); +}; + +/* struct pci_epc_mem_window_s - Address window of the endpoint controller + * + * phys_base: Physical base address of the PCI address window + * size: The size of the PCI address window + * page_size: Size of each page + */ + +struct pci_epc_mem_window_s +{ + uintptr_t phys_base; + size_t size; + size_t page_size; +}; + +/* struct pci_epc_mem_s - Address space of the endpoint controller + * + * phys_base: Physical base address of the PCI address window + * size: The size of the PCI address window + * page_size: Size of each page + * bitmap: Bitmap to manage the PCI address space + * pages: Number of bits representing the address region + * lock: Mutex to protect bitmap + */ + +struct pci_epc_mem_s +{ + uintptr_t phys_base; + size_t size; + size_t page_size; + + FAR unsigned long *bitmap; + int pages; + + /* Mutex to protect against concurrent access for memory allocation */ + + mutex_t lock; +}; + +/* struct pci_epc_ctrl_s - Represents the PCI EPC device + * + * epf: List of endpoint functions present in this EPC device + * ops: Function pointers for performing endpoint operations + * mem: Array of address space of the endpoint controlle + * num_windows: Number of mem supported by device + * max_functions: Max number of functions that can be configured in this EPC + * node: The node of epc list + * lock: Mutex to protect pci_epc ops + * funcno_map: Bitmap to manage physical function number + * name: The current epc structure name that used to bind the epf + */ + +struct pci_epc_ctrl_s +{ + struct list_node epf; + FAR const struct pci_epc_ops_s *ops; + FAR struct pci_epc_mem_s *mem; + unsigned int num_windows; + uint8_t max_functions; + struct list_node node; + + /* Mutex to protect against concurrent access of EP controller */ + + mutex_t lock; + unsigned long funcno_map; + char name[0]; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_get_epc + * + * Description: + * This function is used to get the PCI endpoint controller. + * + * Invoke to get struct pci_epc_ctrl_s * corresponding to the device name + * of the endpoint controller. + * + * Input Parameters: + * epc_name - Device name of the endpoint controller + * + * Returned Value: + * Return epc created if success, NULL if failed + ****************************************************************************/ + +FAR struct pci_epc_ctrl_s *pci_get_epc(FAR const char *epc_name); + +/**************************************************************************** + * Name: pci_epc_get_next_free_bar + * + * Description: + * Helper to get unreserved BAR starting from bar. + * + * Invoke to get the next unreserved BAR starting from barno that can be + * used for endpoint function. For any incorrect value in bar_reserved return + * '0'. + * + * Input Parameters: + * epc_features - pci_epc_features_s structure that holds the reserved bar + * bitmap + * barno - The starting BAR number from where unreserved BAR should + * be searched + * + * Returned Value: + * Return the member of if success, negative if failed + ****************************************************************************/ + +int pci_epc_get_next_free_bar( + FAR const struct pci_epc_features_s *epc_features, int barno); + +/**************************************************************************** + * Name: pci_epc_get_first_free_bar + * + * Description: + * Helper to get first unreserved BAR. + * + * Invoke to get the first unreserved BAR that can be used by the endpoint + * function. For any incorrect value in bar_reserved return '0'. + * + * Input Parameters: + * epc_features - pci_epc_features_s structure that holds the reserved bar + * bitmap + * + * Returned Value: + * Return the member if success, negative if failed + ****************************************************************************/ + +#define pci_epc_get_first_free_bar(f) pci_epc_get_next_free_bar(f, 0) + +/**************************************************************************** + * Name: pci_epc_get_features + * + * Description: + * This function is used to get the features supported by EPC. + * + * Invoke to get the features provided by the EPC which may be + * specific to an endpoint function. Returns pci_epc_features_s on success + * and NULL for any failures. + * + * Input Parameters: + * epc - The features supported by *this* EPC device will be returned + * funcno - The features supported by the EPC device specific to the + * endpoint function with funcno will be returned + * + * Returned Value: + * Epc features if success, NULL if failed + ****************************************************************************/ + +FAR const struct pci_epc_features_s * +pci_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno); + +/**************************************************************************** + * Name: pci_epc_stop + * + * Description: + * This function is used to stop the PCI link. + * + * Invoke to stop the PCI link. + * + * Input Parameters: + * epc - The link of the EPC device that has to be stopped + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_stop(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * Name: pci_epc_start + * + * Description: + * This function is used to start the PCI link. + * + * Invoke to start the PCI link. + * + * Input Parameters: + * epc - The link of *this* EPC device has to be started + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_start(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * Name: pci_epc_raise_irq + * + * Description: + * This function is used to interrupt the host system. + * + * Invoke to raise an legacy, MSI or MSI-X interrupt. + * + * Input Parameters: + * epc - The EPC device which has to interrupt the host + * funcno - The physical endpoint function number in the EPC device + * type - Specify the type of interrupt; legacy, MSI or MSI-X + * interrupt_num - The MSI or MSI-X interrupt number with range (1-N) + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + enum pci_epc_irq_type_e type, uint16_t interrupt_num); + +/**************************************************************************** + * Name: pci_epc_map_msi_irq + * + * Description: + * Map physical address to MSI address and return MSI data. + * + * Invoke to map physical address to MSI address and return MSI data. The + * physical address should be an address in the outbound region. This is + * required to implement doorbell functionality of NTB wherein EPC on either + * side of the interface (primary and secondary) can directly write to the + * physical address (in outbound region) of the other interface to ring + * doorbell. + * + * Input Parameters: + * epc - The EPC device which has to interrupt the host + * funcno - The physical endpoint function number in the EPC + * device + * phys_addr - The physical address of the outbound region + * interrupt_num - The MSI or MSI-X interrupt number with range (1-N) + * entry_size - Size of Outbound address region for each interrupt + * msi_data - The data that should be written in order to raise MSI + * interrupt with interrupt number as 'interrupt num' + * msi_addr_offset - Offset of MSI address from the aligned outbound + * address to which the MSI address is mapped + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_map_msi_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr, uint8_t interrupt_num, + uint32_t entry_size, FAR uint32_t *msi_data, + FAR uint32_t *msi_addr_offset); + +/**************************************************************************** + * Name: pci_epc_get_msi + * + * Description: + * Get the number of MSI interrupt numbers allocated. + * + * Invoke to get the number of MSI interrupts allocated by the RC. + * + * Input Parameters: + * epc - The EPC device to which MSI interrupts was requested + * funcno - The physical endpoint function number in the EPC device + * + * Returned Value: + * Return interrupt number if success, negative if failed + ****************************************************************************/ + +int pci_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno); + +/**************************************************************************** + * Name: pci_epc_set_msi + * + * Description: + * Set the number of MSI interrupt numbers required. + * + * Invoke to set the required number of MSI interrupts. + * + * Input Parameters: + * epc - The EPC device on which MSI has to be configured + * funcno - The physical endpoint function number in the EPC device + * interrupts - Number of MSI interrupts required by the EPF + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uint8_t interrupts); + +/**************************************************************************** + * Name: pci_epc_get_msix + * + * Description: + * Get the number of MSI-X interrupt numbers allocated. + * + * Invoke to get the number of MSI-X interrupts allocated by the RC. + * + * Input Parameters: + * epc - The EPC device to which MSI-X interrupts was requested + * funcno - The physical endpoint function number in the EPC device + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno); + +/**************************************************************************** + * Name: pci_epc_set_msix + * + * Description: + * Set the number of MSI-X interrupt numbers required. + * + * Invoke to set the required number of MSI-X interrupts. + * + * Input Parameters: + * epc - The EPC device on which MSI-X has to be configured + * funcno - The physical endpoint function number in the EPC device + * interrupts - Number of MSI-X interrupts required by the EPF + * barno - BAR where the MSI-X table resides + * offset - Offset pointing to the start of MSI-X table + * + * Returned Value: + * Return interrupt + 1 number if success, negative if failed + ****************************************************************************/ + +int pci_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uint16_t interrupts, int barno, uint32_t offset); + +/**************************************************************************** + * Name: pci_epc_unmap_addr + * + * Description: + * Unmap CPU address from PCI address. + * + * Invoke to unmap the CPU address from PCI address. + * + * Input Parameters: + * epc - The EPC device on which address is allocated + * funcno - The physical endpoint function number in the EPC device + * phys_addr - Physical address of the local systeme + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr); + +/**************************************************************************** + * Name: pci_epc_map_addr + * + * Description: + * Map CPU address to PCI address. + * + * Invoke to map CPU address with PCI address. + * + * Input Parameters: + * epc - The EPC device on which address is allocated + * funcno - The physical endpoint function number in the EPC device + * phys_addr - Physical address of the local system + * pci_addr - PCI address to which the physical address should be mapped + * size - The size of the allocation + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + uintptr_t phys_addr, uint64_t pci_addr, size_t size); + +/**************************************************************************** + * Name: pci_epc_clear_bar + * + * Description: + * Reset the BAR. + * + * Invoke to reset the BAR of the endpoint device. + * + * Input Parameters: + * epc - The EPC device for which the BAR has to be cleared + * funcno - The physical endpoint function number in the EPC device + * bar - The struct bar that contains the BAR information + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_bar_s *bar); + +/**************************************************************************** + * Name: pci_epc_set_bar + * + * Description: + * Configure BAR in order for host to assign PCI addr space. + * + * Invoke to configure the BAR of the endpoint device. + * + * Input Parameters: + * epc - The EPC device on which BAR has to be configured + * funcno - The physical endpoint function number in the EPC device + * bar - The struct pci_epf_bar_s that contains the BAR information + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_bar_s *bar); + +/**************************************************************************** + * Name: pci_epc_write_header + * + * Description: + * Write standard configuration header. + * + * Invoke to write the configuration header to the endpoint controller. + * Every endpoint controller will have a dedicated location to which the + * standard configuration header would be written. The callback function + * should write the header fields to this dedicated location. + * + * Input Parameters: + * epc - The EPC device to which the configuration header should be + * written + * funcno - The physical endpoint function number in the EPC device + * header - Standard configuration header fields + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_write_header(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno, + FAR struct pci_epf_header_s *header); + +/**************************************************************************** + * Name: pci_epc_add_epf + * + * Description: + * This function is used to bind PCI endpoint function to an endpoint + * controller. + * + * A PCI endpoint device can have one or more functions In the case of + * PCIe,the specification allows up to 8 PCIe endpoint functions Invoke + * pci_epc_add_epf() to add a PCI endpoint function to an endpoint + * controller. + * + * Input Parameters: + * epc - The EPC device to which the endpoint function should be added + * epf - The endpoint function to be added + * + * Returned Value: + * Return 0 if success, negative if failed + ****************************************************************************/ + +int pci_epc_add_epf(FAR struct pci_epc_ctrl_s *epc, + FAR struct pci_epf_device_s *epf); + +/**************************************************************************** + * Name: pci_epc_remove_epf + * + * Description: + * This function is used to remove PCI endpoint function from endpoint + * controller. + * + * Invoke to remove PCI endpoint function from the endpoint controller. + * + * Input Parameters: + * epc - The EPC device from which the endpoint function should be removed + * epf - The endpoint function to be removed + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_remove_epf(FAR struct pci_epc_ctrl_s *epc, + FAR struct pci_epf_device_s *epf); + +/**************************************************************************** + * Name: pci_epc_linkup + * + * Description: + * Notify the EPF device that EPC device has established a connection with + * the Root Complex. + * + * Invoke to Notify the EPF device that the EPC device has established a + * connection with the Root Complex. + * + * Input Parameters: + * epc - The EPC device which has established link with the host + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_linkup(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * Name: pci_epc_linkdown + * + * Description: + * Notify the EPF device that EPC device has dropped the connection with + * the Root Complex. + * + * Invoke to Notify the EPF device that the EPC device has dropped the + * connection with the Root Complex. + * + * Input Parameters: + * epc - The EPC device which has dropped the link with the host + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_linkdown(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * Name: pci_epc_init_notify + * + * Description: + * Notify the EPF device that EPC device's core initialization is + * completed. + * + * Invoke to Notify the EPF device that the EPC device's initialization + * is completed. + * + * Input Parameters: + * epc - The EPC device whose core initialization is completed + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_init_notify(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * Name: pci_epc_bme_notify + * + * Description: + * Notify the EPF device that the EPC device has received the BME event + * from the Root complex. + * + * Invoke to Notify the EPF device that the EPC device has received the Bus + * Master Enable (BME) event from the Root complex. + * + * Input Parameters: + * epc - The EPC device that received the BME event + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_bme_notify(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * Name: pci_epc_create + * + * Description: + * This function is used to destroy the EPC device. + * + * Invoke to create a new EPC device and add it to pci_epc class. + * + * Input Parameters: + * name - EPC name strings + * ops - Function pointers for performing EPC operations + * + * Returned Value: + * Return struct pci_epc_ctrl_s * if success, NULL if failed. + ****************************************************************************/ + +FAR struct pci_epc_ctrl_s * +pci_epc_create(FAR const char *name, FAR const struct pci_epc_ops_s *ops); + +/**************************************************************************** + * Name: pci_epc_destroy + * + * Description: + * This function is used to create a new endpoint controller (EPC) device. + * + * Invoke to destroy the PCI EPC device. + * + * Input Parameters: + * epc - The EPC device that has to be destroyed + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_destroy(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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 - The EPC device that invoked pci_epc_mem_exit + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epc_mem_exit(FAR struct pci_epc_ctrl_s *epc); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +#endif /* __INCLUDE_NUTTX_PCI_PCI_EPC_H */ diff --git a/include/nuttx/pci/pci_epf.h b/include/nuttx/pci/pci_epf.h new file mode 100644 index 0000000000..94c49180e1 --- /dev/null +++ b/include/nuttx/pci/pci_epf.h @@ -0,0 +1,315 @@ +/**************************************************************************** + * include/nuttx/pci/pci_epf.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_PCI_PCI_EPF_H +#define __INCLUDE_NUTTX_PCI_PCI_EPF_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* struct pci_epf_header_s - Represents standard configuration header + * + * vendorid: Identifies device manufacturer + * deviceid: Identifies a particular device + * revid: Specifies a device-specific revision identifier + * progif_code: Identifies a specific register-level programming interface + * subclass_code: Identifies more specifically the function of the device + * baseclass_code: Broadly classifies the type of function the device + * performs + * cache_line_size: Specifies the system cacheline size in units of DWORDs + * interrupt_pin: Interrupt pin the device (or device function) uses + * subsys_vendor_id: Vendor of the add-in card or subsystem + * subsys_id: Id specific to vendor + */ + +struct pci_epf_header_s +{ + uint16_t vendorid; + uint16_t deviceid; + uint8_t revid; + uint8_t progif_code; + uint8_t subclass_code; + uint8_t baseclass_code; + uint8_t cache_line_size; + uint8_t interrupt_pin; + uint16_t subsys_vendor_id; + uint16_t subsys_id; +}; + +/* struct pci_epf_bar_s - Represents the BAR of EPF device + * + * phys_addr: Physical address value that should be mapped to the BAR + * addr: Virtual address pointer corresponding to the phys_addr + * size: The size of the address space present in BAR + * barno: BAR number + * flags: Flags that are set for the BAR + */ + +struct pci_epf_bar_s +{ + uintptr_t phys_addr; + FAR void *addr; + size_t size; + int barno; + int flags; +}; + +struct pci_epf_device_id_s +{ + char name[32]; + unsigned long driver_data; +}; + +/* struct pci_epf_device_s - Represents the PCI EPF device + * + * name: The name of the PCI EPF device + * header: Represents standard configuration header + * bar: Represents the BAR of EPF device + * msi_interrupts: Number of MSI interrupts required by this function + * msix_interrupts: Number of MSI-X interrupts required by this function + * funcno: Unique (physical) function number within this endpoint device + * epc: The EPC device to which this EPF device is bound + * driver: The EPF driver to which this EPF device is bound + * id: Pointer to the EPF device ID + * node: To add pci_epf as a list of PCI endpoint functions to pci_epc_ctrl_s + * lock: mutex to protect pci_epf_ops_s + * is_bound: Indicates if bind notification to function driver has been + * invoked + * event_ops: Callbacks for capturing the EPC events + */ + +struct pci_epf_device_s +{ + FAR const char *name; + FAR const char *epc_name; + FAR struct pci_epf_header_s *header; + struct pci_epf_bar_s bar[PCI_STD_NUM_BARS]; + uint8_t msi_interrupts; + uint16_t msix_interrupts; + uint8_t funcno; + + FAR struct pci_epc_ctrl_s *epc; + FAR struct pci_epf_driver_s *driver; + FAR const struct pci_epf_device_id_s *id; + struct list_node node; + + /* Mutex to protect against concurrent access of pci_epf_ops_s */ + + mutex_t lock; + bool is_bound; + FAR const struct pci_epc_event_ops_s *event_ops; +}; + +/* struct pci_epf_ops_s - Set of function pointers for performing EPF + * operations + * + * bind: Ops to perform when a EPC device has been bound to EPF device + * unbind: Ops to perform when a binding has been lost between a EPC device + * and EPF device + */ + +struct pci_epf_ops_s +{ + CODE int (*bind)(FAR struct pci_epf_device_s *epf); + CODE void (*unbind)(FAR struct pci_epf_device_s *epf); +}; + +/* struct pci_epc_event_ops_s - Callbacks for capturing the EPC events + * + * core_init: Callback for the EPC initialization complete event + * link_up: Callback for the EPC link up event + * link_down: Callback for the EPC link down event + * bme: Callback for the EPC BME (Bus Master Enable) event + */ + +struct pci_epc_event_ops_s +{ + CODE int (*core_init)(FAR struct pci_epf_device_s *epf); + CODE int (*link_up)(FAR struct pci_epf_device_s *epf); + CODE int (*link_down)(FAR struct pci_epf_device_s *epf); + CODE int (*bme)(FAR struct pci_epf_device_s *epf); +}; + +/* struct pci_epf_driver_s - Represents the PCI EPF driver + * + * probe: Ops to perform when a new EPF device has been bound to the EPF + * driver + * remove: Ops to perform when the binding between the EPF device and EPF + * driver is broken + * node: EPF driver list node + * ops: Set of function pointers for performing EPF operations + * id_table: Identifies EPF devices for probing + */ + +struct pci_epf_driver_s +{ + CODE int (*probe)(FAR struct pci_epf_device_s *epf); + CODE void (*remove)(FAR struct pci_epf_device_s *epf); + + struct list_node node; + FAR struct pci_epf_ops_s *ops; + FAR const struct pci_epf_device_id_s *id_table; +}; + +/* struct pci_epf_msix_tbl_s - Represents the MSIX table entry structure + * + * msg_addr: Writes to this address will trigger MSIX interrupt in host + * msg_data: Data that should be written to msg_addr to trigger MSIX + * interrupt + * vector_ctrl: Identifies if the function is prohibited from sending a + * message using this MSIX table entry + */ + +struct pci_epf_msix_tbl_s +{ + uint64_t msg_addr; + uint32_t msg_data; + uint32_t vector_ctrl; +}; + +/**************************************************************************** + * Name: pci_epf_device_register + * + * Description: + * This function is used to create a new PCI EPF device. + * + * Invoke to create a new PCI EPF device by providing the name of the + * function device. + * + * Input Parameters: + * epf - The name of the PCI EPF device be used to bind the EPF device to + * a EPF driver + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_device_register(FAR struct pci_epf_device_s *epf); + +/**************************************************************************** + * Name: pci_epf_device_unregister + * + * Description: + * This function is used to unregister a PCI EPF device. + * + * Invoke to unregister the PCI EPF device. + * + * Input Parameters: + * epf - PCI EPF device + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_device_unregister(FAR struct pci_epf_device_s *epf); + +/**************************************************************************** + * Name: pci_epf_register_driver + * + * Description: + * This function is used to register a new PCI EPF driver. + * + * Input Parameters: + * drv - EPF driver + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_register_driver(FAR struct pci_epf_driver_s *drv); + +/**************************************************************************** + * Name: pci_epf_unregister_driver + * + * Description: + * This function is used to unregister the PCI EPF driver. + * + * Invoke to unregister the PCI EPF driver. + * + * Input Parameters: + * drv - The PCI EPF driver that has to be unregistered + * + * Returned Value: + * Return >= 0 if success, < 0 if failed + ****************************************************************************/ + +int pci_epf_unregister_driver(FAR struct pci_epf_driver_s *drv); + +/**************************************************************************** + * Name: pci_epf_alloc_space + * + * Description: + * This function is used to allocate memory for the PCI EPF register + * space. + * + * Invoke to allocate memory for the PCI EPF register space. + * + * Input Parameters: + * epf - The EPF device to whom allocate the memory + * barno - The BAR number corresponding to the allocated register space + * size - The size of the memory that has to be allocated + * align - Alignment size for the allocation region + * + * Returned Value: + * Return space address malloced if success, otherwise NULL + ****************************************************************************/ + +FAR void *pci_epf_alloc_space(FAR struct pci_epf_device_s *epf, int barno, + size_t size, size_t align); + +/**************************************************************************** + * Name: pci_epf_free_space + * + * Description: + * Free the allocated PCI EPF register space + * + * Invoke to free the allocated PCI EPF register space. + * + * Input Parameters: + * epf - The EPF device from whom to free the memory + * barno - The BAR number corresponding to the register space + * addr - The virtual address of the PCI EPF register space + * + * Returned Value: + * None + ****************************************************************************/ + +void pci_epf_free_space(FAR struct pci_epf_device_s *epf, + int barno, FAR void *addr); + +#endif /* __INCLUDE_NUTTX_PCI_PCI_EPF_H */ diff --git a/include/nuttx/pci/pci_regs.h b/include/nuttx/pci/pci_regs.h index f7e168dd14..5eb3331e86 100644 --- a/include/nuttx/pci/pci_regs.h +++ b/include/nuttx/pci/pci_regs.h @@ -115,6 +115,11 @@ #define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ #define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_INTERRUPT_UNKNOWN 0 +#define PCI_INTERRUPT_INTA 1 +#define PCI_INTERRUPT_INTB 2 +#define PCI_INTERRUPT_INTC 3 +#define PCI_INTERRUPT_INTD 4 #define PCI_MIN_GNT 0x3e /* 8 bits */ #define PCI_MAX_LAT 0x3f /* 8 bits */