text: /**
   * @file
   *
   * @brief Public APIs for the PCIe EP drivers.
   */
  
  /*
   * SPDX-License-Identifier: Apache-2.0
   *
   * Copyright 2020 Broadcom
   *
   */
  #ifndef ZEPHYR_INCLUDE_DRIVERS_PCIE_EP_H_
  #define ZEPHYR_INCLUDE_DRIVERS_PCIE_EP_H_
  
  #include <zephyr/device.h>
  #include <init.h>
  #include <kernel.h>
  #include <stdint.h>
  
  enum pcie_ob_mem_type {
  	PCIE_OB_ANYMEM,  /**< PCIe OB window within any address range */
  	PCIE_OB_LOWMEM,  /**< PCIe OB window within 32-bit address range */
  	PCIE_OB_HIGHMEM, /**< PCIe OB window above 32-bit address range */
  };
  
  enum pci_ep_irq_type {
  	PCIE_EP_IRQ_LEGACY,	/**< Raise Legacy interrupt */
  	PCIE_EP_IRQ_MSI,	/**< Raise MSI interrupt */
  	PCIE_EP_IRQ_MSIX,	/**< Raise MSIX interrupt */
  };
  
  enum xfer_direction {
  	HOST_TO_DEVICE,		/**< Read from Host */
  	DEVICE_TO_HOST,		/**< Write to Host */
  };
  
  enum pcie_reset {
  	PCIE_PERST,	/**< Cold reset */
  	PCIE_PERST_INB,	/**< Inband hot reset */
  	PCIE_FLR,	/**< Functional Level Reset */
  	PCIE_RESET_MAX
  };
  
  /**
   * @typedef pcie_ep_reset_callback_t
   * @brief Callback API for PCIe reset interrupts
   *
   * These callbacks execute in interrupt context. Therefore, use only
   * interrupt-safe APIS. Registration of callbacks is done via
   * @a pcie_ep_register_reset_cb
   *
   * @param arg Pointer provided at registration time, later to be
   *	  passed back as argument to callback function
   */
  
  typedef void (*pcie_ep_reset_callback_t)(void *arg);
  
  struct pcie_ep_driver_api {
  	int (*conf_read)(const struct device *dev, uint32_t offset,
  			 uint32_t *data);
  	void (*conf_write)(const struct device *dev, uint32_t offset,
  			   uint32_t data);
  	int (*map_addr)(const struct device *dev, uint64_t pcie_addr,
  			uint64_t *mapped_addr, uint32_t size,
  			enum pcie_ob_mem_type ob_mem_type);
  	void (*unmap_addr)(const struct device *dev, uint64_t mapped_addr);
  	int (*raise_irq)(const struct device *dev,
  			 enum pci_ep_irq_type irq_type,
  			 uint32_t irq_num);
  	int (*register_reset_cb)(const struct device *dev,
  				 enum pcie_reset reset,
  				 pcie_ep_reset_callback_t cb, void *arg);
  	int (*dma_xfer)(const struct device *dev, uint64_t mapped_addr,
  			uintptr_t local_addr, uint32_t size,
  			enum xfer_direction dir);
  };
  
  /**
   * @brief Read PCIe EP configuration space
   *
   * @details This API reads EP's own configuration space
   *
   * @param dev    Pointer to the device structure for the driver instance
   * @param offset Offset within configuration space
   * @param data   Pointer to data read from the offset
   *
   * @return 0 if successful, negative errno code if failure.
   */
  
  static inline int pcie_ep_conf_read(const struct device *dev,
  				    uint32_t offset, uint32_t *data)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  
  	return api->conf_read(dev, offset, data);
  }
  
  /**
   * @brief Write PCIe EP configuration space
   *
   * @details This API writes EP's own configuration space
   *
   * @param dev    Pointer to the device structure for the driver instance
   * @param offset Offset within configuration space
   * @param data   Data to be written at the offset
   */
  
  static inline void pcie_ep_conf_write(const struct device *dev,
  				      uint32_t offset, uint32_t data)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  
  	api->conf_write(dev, offset, data);
  }
  
  /**
   * @brief Map a host memory buffer to PCIe outbound region
   *
   * @details This API maps a host memory buffer to PCIe outbound region,
   *	    It is left to EP driver to manage multiple mappings through
   *	    multiple PCIe outbound regions if supported by SoC
   *
   * @param dev         Pointer to the device structure for the driver instance
   * @param pcie_addr   Host memory buffer address to be mapped
   * @param mapped_addr Mapped PCIe outbound region address
   * @param size        Host memory buffer size (bytes)
   * @param ob_mem_type Hint if lowmem/highmem outbound region has to be used,
   *		      this is useful in cases where bus master cannot generate
   *		      more than 32-bit address; it becomes essential to use
   *		      lowmem outbound region
   *
   * @return Mapped size : If mapped size is less than requested size,
   *	   then requestor has to call the same API again to map
   *	   the unmapped host buffer after data transfer is done with
   *	   mapped size. This situation may arise because of the
   *	   mapping alignment requirements.
   *
   * @return Negative errno code if failure.
   */
  
  static inline int pcie_ep_map_addr(const struct device *dev,
  				   uint64_t pcie_addr,
  				   uint64_t *mapped_addr, uint32_t size,
  				   enum pcie_ob_mem_type ob_mem_type)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  
  	return api->map_addr(dev, pcie_addr, mapped_addr, size, ob_mem_type);
  }
  
  /**
   * @brief Remove mapping to PCIe outbound region
   *
   * @details This API removes mapping to PCIe outbound region.
   *	    Mapped PCIe outbound region address is given as argument
   *	    to figure out the outbound region to be unmapped
   *
   * @param dev         Pointer to the device structure for the driver instance
   * @param mapped_addr PCIe outbound region address
   */
  
  static inline void pcie_ep_unmap_addr(const struct device *dev,
  				      uint64_t mapped_addr)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  
  	api->unmap_addr(dev, mapped_addr);
  }
  
  /**
   * @brief Raise interrupt to Host
   *
   * @details This API raises interrupt to Host
   *
   * @param   dev      Pointer to the device structure for the driver instance
   * @param   irq_type Type of Interrupt be raised (legacy, MSI or MSI-X)
   * @param   irq_num  MSI or MSI-X interrupt number
   *
   * @return 0 if successful, negative errno code if failure.
   */
  
  static inline int pcie_ep_raise_irq(const struct device *dev,
  				    enum pci_ep_irq_type irq_type,
  				    uint32_t irq_num)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  	return api->raise_irq(dev, irq_type, irq_num);
  }
  
  /**
   * @brief Register callback function for reset interrupts
   *
   * @details If reset interrupts are handled by device, this API can be
   *	    used to register callback function, which will be
   *	    executed part of corresponding PCIe reset handler
   *
   * @param   dev   Pointer to the device structure for the driver instance
   * @param   reset Reset interrupt type
   * @param   cb    Callback function being registered
   * @param   arg   Argument to be passed back to callback function
   *
   * @return 0 if successful, negative errno code if failure.
   */
  
  static inline int pcie_ep_register_reset_cb(const struct device *dev,
  					    enum pcie_reset reset,
  					    pcie_ep_reset_callback_t cb,
  					    void *arg)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  
  	if (api->register_reset_cb) {
  		return api->register_reset_cb(dev, reset, cb, arg);
  	}
  
  	return -ENOTSUP;
  }
  
  /**
   * @brief Data transfer between mapped Host memory and device memory with
   *	  "System DMA". The term "System DMA" is used to clarify that we
   *	  are not talking about dedicated "PCIe DMA"; rather the one
   *	  which does not understand PCIe address directly, and
   *	  uses the mapped Host memory.
   *
   * @details If DMA controller is available in the EP device, this API can be
   *	    used to achieve data transfer between mapped Host memory,
   *	    i.e. outbound memory and EP device's local memory with DMA
   *
   * @param   dev         Pointer to the device structure for the driver instance
   * @param   mapped_addr Mapped Host memory address
   * @param   local_addr  Device memory address
   * @param   size        DMA transfer length (bytes)
   * @param   dir         Direction of DMA transfer
   *
   * @return 0 if successful, negative errno code if failure.
   */
  
  static inline int pcie_ep_dma_xfer(const struct device *dev,
  				   uint64_t mapped_addr,
  				   uintptr_t local_addr, uint32_t size,
  				   const enum xfer_direction dir)
  {
  	const struct pcie_ep_driver_api *api =
  			(const struct pcie_ep_driver_api *)dev->api;
  
  	if (api->dma_xfer) {
  		return api->dma_xfer(dev, mapped_addr, local_addr, size, dir);
  	}
  
  	return -ENOTSUP;
  }
  
  /**
   * @brief Data transfer using memcpy
   *
   * @details Helper API to achieve data transfer with memcpy
   *          through PCIe outbound memory
   *
   * @param dev         Pointer to the device structure for the driver instance
   * @param pcie_addr   Host memory buffer address
   * @param local_addr  Local memory buffer address
   * @param size        Data transfer size (bytes)
   * @param ob_mem_type Hint if lowmem/highmem outbound region has to be used
   *                    (PCIE_OB_LOWMEM / PCIE_OB_HIGHMEM / PCIE_OB_ANYMEM),
   *                    should be PCIE_OB_LOWMEM if bus master cannot generate
   *                    more than 32-bit address
   * @param dir         Data transfer direction (HOST_TO_DEVICE / DEVICE_TO_HOST)
   *
   * @return 0 if successful, negative errno code if failure.
   */
  int pcie_ep_xfer_data_memcpy(const struct device *dev, uint64_t pcie_addr,
  			     uintptr_t *local_addr, uint32_t size,
  			     enum pcie_ob_mem_type ob_mem_type,
  			     enum xfer_direction dir);
  
  /**
   * @brief Data transfer using system DMA
   *
   * @details Helper API to achieve data transfer with system DMA through PCIe
   *          outbound memory, this API is based off pcie_ep_xfer_data_memcpy,
   *          here we use "system dma" instead of memcpy
   *
   * @param dev         Pointer to the device structure for the driver instance
   * @param pcie_addr   Host memory buffer address
   * @param local_addr  Local memory buffer address
   * @param size        Data transfer size (bytes)
   * @param ob_mem_type Hint if lowmem/highmem outbound region has to be used
   *                    (PCIE_OB_LOWMEM / PCIE_OB_HIGHMEM / PCIE_OB_ANYMEM)
   * @param dir         Data transfer direction (HOST_TO_DEVICE / DEVICE_TO_HOST)
   *
   * @return 0 if successful, negative errno code if failure.
   */
  int pcie_ep_xfer_data_dma(const struct device *dev, uint64_t pcie_addr,
  			  uintptr_t *local_addr, uint32_t size,
  			  enum pcie_ob_mem_type ob_mem_type,
  			  enum xfer_direction dir);
  
  #endif /* ZEPHYR_INCLUDE_DRIVERS_PCIE_EP_H_ */
  