drivers: pcie: Add support for IRQ allocation management
There are x86 platforms where the IRQ configuration register for PCIe is not pre-populated and the OS needs to assign a number dynamically by writing to the register. In order to allocate interrupts we have to know which ones have been hard-coded in device tree. We accomplish this by collecting these values through the IRQ_CONNECT() macro and placing them in a dedicated linker section (in ROM). The full set of allocated interrupts are managed through a bitmap, and the pre-allocated values (from the linker section) are inserted into this upon initial runtime access. This patch introduces a new pcie_alloc_irq() API that drivers can use to allocate interrupt line numbers. The two in-tree drivers that were using this API (I2C and UART) are converted to use the new API. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
c8bbd412c7
commit
9e4dfd8f4e
4 changed files with 107 additions and 0 deletions
|
@ -68,6 +68,83 @@ uintptr_t pcie_get_mbar(pcie_bdf_t bdf, unsigned int index)
|
|||
return PCIE_CONF_BAR_ADDR(addr);
|
||||
}
|
||||
|
||||
/* The first bit is used to indicate whether the list of reserved interrupts
|
||||
* have been initialized based on content stored in the irq_alloc linker
|
||||
* section in ROM.
|
||||
*/
|
||||
#define IRQ_LIST_INITIALIZED 0
|
||||
|
||||
static ATOMIC_DEFINE(irq_reserved, CONFIG_MAX_IRQ_LINES);
|
||||
|
||||
static unsigned int irq_alloc(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(irq_reserved); i++) {
|
||||
unsigned int fz, irq;
|
||||
|
||||
while ((fz = find_lsb_set(~atomic_get(&irq_reserved[i])))) {
|
||||
irq = (fz - 1) + (i * sizeof(atomic_val_t) * 8);
|
||||
if (irq >= CONFIG_MAX_IRQ_LINES) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!atomic_test_and_set_bit(irq_reserved, irq)) {
|
||||
return irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PCIE_CONF_INTR_IRQ_NONE;
|
||||
}
|
||||
|
||||
static bool irq_is_reserved(unsigned int irq)
|
||||
{
|
||||
return atomic_test_bit(irq_reserved, irq);
|
||||
}
|
||||
|
||||
static void irq_init(void)
|
||||
{
|
||||
extern uint8_t __irq_alloc_start[];
|
||||
extern uint8_t __irq_alloc_end[];
|
||||
const uint8_t *irq;
|
||||
|
||||
for (irq = __irq_alloc_start; irq < __irq_alloc_end; irq++) {
|
||||
__ASSERT_NO_MSG(*irq < CONFIG_MAX_IRQ_LINES);
|
||||
atomic_set_bit(irq_reserved, *irq);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int pcie_alloc_irq(pcie_bdf_t bdf)
|
||||
{
|
||||
unsigned int irq;
|
||||
uint32_t data;
|
||||
|
||||
if (!atomic_test_and_set_bit(irq_reserved, IRQ_LIST_INITIALIZED)) {
|
||||
irq_init();
|
||||
}
|
||||
|
||||
data = pcie_conf_read(bdf, PCIE_CONF_INTR);
|
||||
irq = PCIE_CONF_INTR_IRQ(data);
|
||||
|
||||
if (irq == PCIE_CONF_INTR_IRQ_NONE || irq >= CONFIG_MAX_IRQ_LINES ||
|
||||
irq_is_reserved(irq)) {
|
||||
|
||||
irq = irq_alloc();
|
||||
if (irq == PCIE_CONF_INTR_IRQ_NONE) {
|
||||
return irq;
|
||||
}
|
||||
|
||||
data &= ~0xffU;
|
||||
data |= irq;
|
||||
pcie_conf_write(bdf, PCIE_CONF_INTR, data);
|
||||
} else {
|
||||
atomic_set_bit(irq_reserved, irq);
|
||||
}
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
unsigned int pcie_wired_irq(pcie_bdf_t bdf)
|
||||
{
|
||||
uint32_t data = pcie_conf_read(bdf, PCIE_CONF_INTR);
|
||||
|
|
|
@ -93,11 +93,20 @@ struct x86_ssf {
|
|||
|
||||
#endif /* _ASMLANGUAGE */
|
||||
|
||||
#ifdef CONFIG_PCIE
|
||||
#define X86_RESERVE_IRQ(irq_p, name) \
|
||||
static Z_DECL_ALIGN(uint8_t) name \
|
||||
__in_section(_irq_alloc, static, name) __used = irq_p
|
||||
#else
|
||||
#define X86_RESERVE_IRQ(irq_p, name)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* All Intel64 interrupts are dynamically connected.
|
||||
*/
|
||||
|
||||
#define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
|
||||
X86_RESERVE_IRQ(irq_p, _CONCAT(_irq_alloc_fixed, __COUNTER__)); \
|
||||
arch_irq_connect_dynamic(irq_p, priority_p, \
|
||||
(void (*)(const void *))isr_p, \
|
||||
isr_param_p, flags_p)
|
||||
|
|
|
@ -93,6 +93,18 @@ extern uintptr_t pcie_get_mbar(pcie_bdf_t bdf, unsigned int index);
|
|||
*/
|
||||
extern void pcie_set_cmd(pcie_bdf_t bdf, uint32_t bits, bool on);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Return the IRQ assigned by the firmware/board to an endpoint.
|
||||
*
|
||||
|
|
|
@ -138,6 +138,15 @@
|
|||
Z_ITERABLE_SECTION_ROM(dns_sd_rec, 4)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PCIE)
|
||||
SECTION_DATA_PROLOGUE(irq_alloc,,)
|
||||
{
|
||||
__irq_alloc_start = .;
|
||||
KEEP(*(SORT_BY_NAME("._irq_alloc*")));
|
||||
__irq_alloc_end = .;
|
||||
} GROUP_LINK_IN(ROMABLE_REGION)
|
||||
#endif /* CONFIG_PCIE */
|
||||
|
||||
SECTION_DATA_PROLOGUE(log_const_sections,,)
|
||||
{
|
||||
__log_const_start = .;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue