|  | /* | 
|  | * Copyright (c) 2019 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #ifndef ZEPHYR_INCLUDE_DRIVERS_PCIE_PCIE_H_ | 
|  | #define ZEPHYR_INCLUDE_DRIVERS_PCIE_PCIE_H_ | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <dt-bindings/pcie/pcie.h> | 
|  | #include <zephyr/types.h> | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | extern "C" { | 
|  | #endif | 
|  |  | 
|  | /** | 
|  | * @typedef pcie_bdf_t | 
|  | * @brief A unique PCI(e) endpoint (bus, device, function). | 
|  | * | 
|  | * A PCI(e) endpoint is uniquely identified topologically using a | 
|  | * (bus, device, function) tuple. The internal structure is documented | 
|  | * in include/dt-bindings/pcie/pcie.h: see PCIE_BDF() and friends, since | 
|  | * these tuples are referenced from devicetree. | 
|  | */ | 
|  | typedef uint32_t pcie_bdf_t; | 
|  |  | 
|  | /** | 
|  | * @typedef pcie_id_t | 
|  | * @brief A unique PCI(e) identifier (vendor ID, device ID). | 
|  | * | 
|  | * The PCIE_CONF_ID register for each endpoint is a (vendor ID, device ID) | 
|  | * pair, which is meant to tell the system what the PCI(e) endpoint is. Again, | 
|  | * look to PCIE_ID_* macros in include/dt-bindings/pcie/pcie.h for more. | 
|  | */ | 
|  | typedef uint32_t pcie_id_t; | 
|  |  | 
|  | struct pcie_mbar { | 
|  | uintptr_t phys_addr; | 
|  | size_t size; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * These functions are arch-, board-, or SoC-specific. | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @brief Look up the BDF based on PCI(e) vendor & device ID | 
|  | * | 
|  | * This function is used to look up the BDF for a device given its | 
|  | * vendor and device ID. | 
|  | * | 
|  | * @param id PCI(e) vendor & device ID encoded using PCIE_ID() | 
|  | * @return The BDF for the device, or PCIE_BDF_NONE if it was not found | 
|  | */ | 
|  | extern pcie_bdf_t pcie_bdf_lookup(pcie_id_t id); | 
|  |  | 
|  | /** | 
|  | * @brief Read a 32-bit word from an endpoint's configuration space. | 
|  | * | 
|  | * This function is exported by the arch/SoC/board code. | 
|  | * | 
|  | * @param bdf PCI(e) endpoint | 
|  | * @param reg the configuration word index (not address) | 
|  | * @return the word read (0xFFFFFFFFU if nonexistent endpoint or word) | 
|  | */ | 
|  | extern uint32_t pcie_conf_read(pcie_bdf_t bdf, unsigned int reg); | 
|  |  | 
|  | /** | 
|  | * @brief Write a 32-bit word to an endpoint's configuration space. | 
|  | * | 
|  | * This function is exported by the arch/SoC/board code. | 
|  | * | 
|  | * @param bdf PCI(e) endpoint | 
|  | * @param reg the configuration word index (not address) | 
|  | * @param data the value to write | 
|  | */ | 
|  | extern void pcie_conf_write(pcie_bdf_t bdf, unsigned int reg, uint32_t data); | 
|  |  | 
|  | /** | 
|  | * @brief Probe for the presence of a PCI(e) endpoint. | 
|  | * | 
|  | * @param bdf the endpoint to probe | 
|  | * @param id the endpoint ID to expect, or PCIE_ID_NONE for "any device" | 
|  | * @return true if the device is present, false otherwise | 
|  | */ | 
|  | extern bool pcie_probe(pcie_bdf_t bdf, pcie_id_t id); | 
|  |  | 
|  | /** | 
|  | * @brief Get the MBAR at a specific BAR index | 
|  | * @param bdf the PCI(e) endpoint | 
|  | * @param bar_index 0-based BAR index | 
|  | * @param mbar Pointer to struct pcie_mbar | 
|  | * @return true if the mbar was found and is valid, false otherwise | 
|  | */ | 
|  | extern bool pcie_get_mbar(pcie_bdf_t bdf, | 
|  | unsigned int bar_index, | 
|  | struct pcie_mbar *mbar); | 
|  |  | 
|  | /** | 
|  | * @brief Probe the nth MMIO address assigned to an endpoint. | 
|  | * @param bdf the PCI(e) endpoint | 
|  | * @param index (0-based) index | 
|  | * @param mbar Pointer to struct pcie_mbar | 
|  | * @return true if the mbar was found and is valid, false otherwise | 
|  | * | 
|  | * A PCI(e) endpoint has 0 or more memory-mapped regions. This function | 
|  | * allows the caller to enumerate them by calling with index=0..n. | 
|  | * Value of n has to be below 6, as there is a maximum of 6 BARs. The indices | 
|  | * are order-preserving with respect to the endpoint BARs: e.g., index 0 | 
|  | * will return the lowest-numbered memory BAR on the endpoint. | 
|  | */ | 
|  | extern bool pcie_probe_mbar(pcie_bdf_t bdf, | 
|  | unsigned int index, | 
|  | struct pcie_mbar *mbar); | 
|  |  | 
|  | /** | 
|  | * @brief Set or reset bits in the endpoint command/status register. | 
|  | * | 
|  | * @param bdf the PCI(e) endpoint | 
|  | * @param bits the powerset of bits of interest | 
|  | * @param on use true to set bits, false to reset them | 
|  | */ | 
|  | extern void pcie_set_cmd(pcie_bdf_t bdf, uint32_t bits, bool on); | 
|  |  | 
|  | #ifndef CONFIG_PCIE_CONTROLLER | 
|  | /** | 
|  | * @brief Allocate an IRQ for an endpoint. | 
|  | * | 
|  | * This function first checks the IRQ register and if it contains a valid | 
|  | * value this is returned. If the register does not contain a valid value | 
|  | * allocation of a new one is attempted. | 
|  | * Such function is only exposed if CONFIG_PCIE_CONTROLLER is unset. | 
|  | * It is thus available where architecture tied dynamic IRQ allocation for | 
|  | * PCIe device makes sense. | 
|  | * | 
|  | * @param bdf the PCI(e) endpoint | 
|  | * @return the IRQ number, or PCIE_CONF_INTR_IRQ_NONE if allocation failed. | 
|  | */ | 
|  | extern unsigned int pcie_alloc_irq(pcie_bdf_t bdf); | 
|  | #endif /* CONFIG_PCIE_CONTROLLER */ | 
|  |  | 
|  | /** | 
|  | * @brief Return the IRQ assigned by the firmware/board to an endpoint. | 
|  | * | 
|  | * @param bdf the PCI(e) endpoint | 
|  | * @return the IRQ number, or PCIE_CONF_INTR_IRQ_NONE if unknown. | 
|  | */ | 
|  | extern unsigned int pcie_get_irq(pcie_bdf_t bdf); | 
|  |  | 
|  | /** | 
|  | * @brief Enable the PCI(e) endpoint to generate the specified IRQ. | 
|  | * | 
|  | * @param bdf the PCI(e) endpoint | 
|  | * @param irq the IRQ to generate | 
|  | * | 
|  | * If MSI is enabled and the endpoint supports it, the endpoint will | 
|  | * be configured to generate the specified IRQ via MSI. Otherwise, it | 
|  | * is assumed that the IRQ has been routed by the boot firmware | 
|  | * to the specified IRQ, and the IRQ is enabled (at the I/O APIC, or | 
|  | * wherever appropriate). | 
|  | */ | 
|  | extern void pcie_irq_enable(pcie_bdf_t bdf, unsigned int irq); | 
|  |  | 
|  | /** | 
|  | * @brief Find a PCI(e) capability in an endpoint's configuration space. | 
|  | * | 
|  | * @param bdf the PCI endpoint to examine | 
|  | * @param cap_id the capability ID of interest | 
|  | * @return the index of the configuration word, or 0 if no capability. | 
|  | */ | 
|  | extern uint32_t pcie_get_cap(pcie_bdf_t bdf, uint32_t cap_id); | 
|  |  | 
|  | /** | 
|  | * @brief Find an Extended PCI(e) capability in an endpoint's configuration space. | 
|  | * | 
|  | * @param bdf the PCI endpoint to examine | 
|  | * @param cap_id the capability ID of interest | 
|  | * @return the index of the configuration word, or 0 if no capability. | 
|  | */ | 
|  | extern uint32_t pcie_get_ext_cap(pcie_bdf_t bdf, uint32_t cap_id); | 
|  |  | 
|  | /** | 
|  | * @brief Dynamically connect a PCIe endpoint IRQ to an ISR handler | 
|  | * | 
|  | * @param bdf the PCI endpoint to examine | 
|  | * @param irq the IRQ to connect (see pcie_alloc_irq()) | 
|  | * @param priority priority of the IRQ | 
|  | * @param routine the ISR handler to connect to the IRQ | 
|  | * @param parameter the parameter to provide to the handler | 
|  | * @param flags IRQ connection flags | 
|  | * @return true if connected, false otherwise | 
|  | */ | 
|  | extern bool pcie_connect_dynamic_irq(pcie_bdf_t bdf, | 
|  | unsigned int irq, | 
|  | unsigned int priority, | 
|  | void (*routine)(const void *parameter), | 
|  | const void *parameter, | 
|  | uint32_t flags); | 
|  |  | 
|  | /* | 
|  | * Configuration word 13 contains the head of the capabilities list. | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_CAPPTR	13U	/* capabilities pointer */ | 
|  | #define PCIE_CONF_CAPPTR_FIRST(w)	(((w) >> 2) & 0x3FU) | 
|  |  | 
|  | /* | 
|  | * The first word of every capability contains a capability identifier, | 
|  | * and a link to the next capability (or 0) in configuration space. | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_CAP_ID(w)		((w) & 0xFFU) | 
|  | #define PCIE_CONF_CAP_NEXT(w)		(((w) >> 10) & 0x3FU) | 
|  |  | 
|  | /* | 
|  | * The extended PCI Express capabilies lies at the end of the PCI configuration space | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_EXT_CAPPTR	64U | 
|  |  | 
|  | /* | 
|  | * The first word of every capability contains an extended capability identifier, | 
|  | * and a link to the next capability (or 0) in the extended configuration space. | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_EXT_CAP_ID(w)		((w) & 0xFFFFU) | 
|  | #define PCIE_CONF_EXT_CAP_VER(w)	(((w) >> 16) & 0xFU) | 
|  | #define PCIE_CONF_EXT_CAP_NEXT(w)	(((w) >> 20) & 0xFFFU) | 
|  |  | 
|  | /* | 
|  | * Configuration word 0 aligns directly with pcie_id_t. | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_ID		0U | 
|  |  | 
|  | /* | 
|  | * Configuration word 1 contains command and status bits. | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_CMDSTAT	1U	/* command/status register */ | 
|  |  | 
|  | #define PCIE_CONF_CMDSTAT_IO		0x00000001U  /* I/O access enable */ | 
|  | #define PCIE_CONF_CMDSTAT_MEM		0x00000002U  /* mem access enable */ | 
|  | #define PCIE_CONF_CMDSTAT_MASTER	0x00000004U  /* bus master enable */ | 
|  | #define PCIE_CONF_CMDSTAT_CAPS		0x00100000U  /* capabilities list */ | 
|  |  | 
|  | /* | 
|  | * Configuration word 2 has additional function identification that | 
|  | * we only care about for debug output (PCIe shell commands). | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_CLASSREV	2U	/* class/revision register */ | 
|  |  | 
|  | #define PCIE_CONF_CLASSREV_CLASS(w)	(((w) >> 24) & 0xFFU) | 
|  | #define PCIE_CONF_CLASSREV_SUBCLASS(w)  (((w) >> 16) & 0xFFU) | 
|  | #define PCIE_CONF_CLASSREV_PROGIF(w)	(((w) >> 8) & 0xFFU) | 
|  | #define PCIE_CONF_CLASSREV_REV(w)	((w) & 0xFFU) | 
|  |  | 
|  | /* | 
|  | * The only part of configuration word 3 that is of interest to us is | 
|  | * the header type, as we use it to distinguish functional endpoints | 
|  | * from bridges (which are, for our purposes, transparent). | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_TYPE		3U | 
|  |  | 
|  | #define PCIE_CONF_MULTIFUNCTION(w)	(((w) & 0x00800000U) != 0U) | 
|  | #define PCIE_CONF_TYPE_BRIDGE(w)	(((w) & 0x007F0000U) != 0U) | 
|  |  | 
|  | /* | 
|  | * Words 4-9 are BARs are I/O or memory decoders. Memory decoders may | 
|  | * be 64-bit decoders, in which case the next configuration word holds | 
|  | * the high-order bits (and is, thus, not a BAR itself). | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_BAR0		4U | 
|  | #define PCIE_CONF_BAR1		5U | 
|  | #define PCIE_CONF_BAR2		6U | 
|  | #define PCIE_CONF_BAR3		7U | 
|  | #define PCIE_CONF_BAR4		8U | 
|  | #define PCIE_CONF_BAR5		9U | 
|  |  | 
|  | #define PCIE_CONF_BAR_IO(w)		(((w) & 0x00000001U) == 0x00000001U) | 
|  | #define PCIE_CONF_BAR_MEM(w)		(((w) & 0x00000001U) != 0x00000001U) | 
|  | #define PCIE_CONF_BAR_64(w)		(((w) & 0x00000006U) == 0x00000004U) | 
|  | #define PCIE_CONF_BAR_ADDR(w)		((w) & ~0xfUL) | 
|  | #define PCIE_CONF_BAR_IO_ADDR(w)	((w) & ~0x3UL) | 
|  | #define PCIE_CONF_BAR_FLAGS(w)		((w) & 0xfUL) | 
|  | #define PCIE_CONF_BAR_NONE		0U | 
|  |  | 
|  | #define PCIE_CONF_BAR_INVAL		0xFFFFFFF0U | 
|  | #define PCIE_CONF_BAR_INVAL64		0xFFFFFFFFFFFFFFF0UL | 
|  |  | 
|  | #define PCIE_CONF_BAR_INVAL_FLAGS(w)			\ | 
|  | ((((w) & 0x00000006U) == 0x00000006U) ||	\ | 
|  | (((w) & 0x00000006U) == 0x00000002U)) | 
|  |  | 
|  | /* | 
|  | * Type 1 Header has files related to bus management | 
|  | */ | 
|  | #define PCIE_BUS_NUMBER         6U | 
|  |  | 
|  | #define PCIE_BUS_PRIMARY_NUMBER(w)      ((w) & 0xffUL) | 
|  | #define PCIE_BUS_SECONDARY_NUMBER(w)    (((w) >> 8) & 0xffUL) | 
|  | #define PCIE_BUS_SUBORDINATE_NUMBER(w)  (((w) >> 16) & 0xffUL) | 
|  | #define PCIE_SECONDARY_LATENCY_TIMER(w) (((w) >> 24) & 0xffUL) | 
|  |  | 
|  | #define PCIE_BUS_NUMBER_VAL(prim, sec, sub, lat) \ | 
|  | (((prim) & 0xffUL) |			 \ | 
|  | (((sec) & 0xffUL) << 8) |		 \ | 
|  | (((sub) & 0xffUL) << 16) |		 \ | 
|  | (((lat) & 0xffUL) << 24)) | 
|  |  | 
|  | /* | 
|  | * Type 1 words 7 to 12 setups Bridge Memory base and limits | 
|  | */ | 
|  | #define PCIE_IO_SEC_STATUS      7U | 
|  |  | 
|  | #define PCIE_IO_BASE(w)         ((w) & 0xffUL) | 
|  | #define PCIE_IO_LIMIT(w)        (((w) >> 8) & 0xffUL) | 
|  | #define PCIE_SEC_STATUS(w)      (((w) >> 16) & 0xffffUL) | 
|  |  | 
|  | #define PCIE_IO_SEC_STATUS_VAL(iob, iol, sec_status) \ | 
|  | (((iob) & 0xffUL) |			     \ | 
|  | (((iol) & 0xffUL) << 8) |		     \ | 
|  | (((sec_status) & 0xffffUL) << 16)) | 
|  |  | 
|  | #define PCIE_MEM_BASE_LIMIT     8U | 
|  |  | 
|  | #define PCIE_MEM_BASE(w)        ((w) & 0xffffUL) | 
|  | #define PCIE_MEM_LIMIT(w)       (((w) >> 16) & 0xffffUL) | 
|  |  | 
|  | #define PCIE_MEM_BASE_LIMIT_VAL(memb, meml) \ | 
|  | (((memb) & 0xffffUL) |		    \ | 
|  | (((meml) & 0xffffUL) << 16)) | 
|  |  | 
|  | #define PCIE_PREFETCH_BASE_LIMIT        9U | 
|  |  | 
|  | #define PCIE_PREFETCH_BASE(w)   ((w) & 0xffffUL) | 
|  | #define PCIE_PREFETCH_LIMIT(w)  (((w) >> 16) & 0xffffUL) | 
|  |  | 
|  | #define PCIE_PREFETCH_BASE_LIMIT_VAL(pmemb, pmeml) \ | 
|  | (((pmemb) & 0xffffUL) |			   \ | 
|  | (((pmeml) & 0xffffUL) << 16)) | 
|  |  | 
|  | #define PCIE_PREFETCH_BASE_UPPER        10U | 
|  |  | 
|  | #define PCIE_PREFETCH_LIMIT_UPPER       11U | 
|  |  | 
|  | #define PCIE_IO_BASE_LIMIT_UPPER        12U | 
|  |  | 
|  | #define PCIE_IO_BASE_UPPER(w)   ((w) & 0xffffUL) | 
|  | #define PCIE_IO_LIMIT_UPPER(w)  (((w) >> 16) & 0xffffUL) | 
|  |  | 
|  | #define PCIE_IO_BASE_LIMIT_UPPER_VAL(iobu, iolu) \ | 
|  | (((iobu) & 0xffffUL) |			 \ | 
|  | (((iolu) & 0xffffUL) << 16)) | 
|  |  | 
|  | /* | 
|  | * Word 15 contains information related to interrupts. | 
|  | * | 
|  | * We're only interested in the low byte, which is [supposed to be] set by | 
|  | * the firmware to indicate which wire IRQ the device interrupt is routed to. | 
|  | */ | 
|  |  | 
|  | #define PCIE_CONF_INTR		15U | 
|  |  | 
|  | #define PCIE_CONF_INTR_IRQ(w)	((w) & 0xFFU) | 
|  | #define PCIE_CONF_INTR_IRQ_NONE	0xFFU  /* no interrupt routed */ | 
|  |  | 
|  | #define PCIE_MAX_BUS  (0xFFFFFFFF & PCIE_BDF_BUS_MASK) | 
|  | #define PCIE_MAX_DEV  (0xFFFFFFFF & PCIE_BDF_DEV_MASK) | 
|  | #define PCIE_MAX_FUNC (0xFFFFFFFF & PCIE_BDF_FUNC_MASK) | 
|  |  | 
|  | /** | 
|  | * @brief Initialize an interrupt handler for a PCIe endpoint IRQ | 
|  | * | 
|  | * This routine is only meant to be used by drivers using PCIe bus and having | 
|  | * fixed or MSI based IRQ (so no runtime detection of the IRQ). In case | 
|  | * of runtime detection see pcie_connect_dynamic_irq() | 
|  | * | 
|  | * @param bdf_p PCIe endpoint BDF | 
|  | * @param irq_p IRQ line number. | 
|  | * @param priority_p Interrupt priority. | 
|  | * @param isr_p Address of interrupt service routine. | 
|  | * @param isr_param_p Parameter passed to interrupt service routine. | 
|  | * @param flags_p Architecture-specific IRQ configuration flags.. | 
|  | */ | 
|  | #define PCIE_IRQ_CONNECT(bdf_p, irq_p, priority_p,			\ | 
|  | isr_p, isr_param_p, flags_p)			\ | 
|  | ARCH_PCIE_IRQ_CONNECT(bdf_p, irq_p, priority_p,			\ | 
|  | isr_p, isr_param_p, flags_p) | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif /* ZEPHYR_INCLUDE_DRIVERS_PCIE_PCIE_H_ */ |