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);
|
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)
|
unsigned int pcie_wired_irq(pcie_bdf_t bdf)
|
||||||
{
|
{
|
||||||
uint32_t data = pcie_conf_read(bdf, PCIE_CONF_INTR);
|
uint32_t data = pcie_conf_read(bdf, PCIE_CONF_INTR);
|
||||||
|
|
|
@ -93,11 +93,20 @@ struct x86_ssf {
|
||||||
|
|
||||||
#endif /* _ASMLANGUAGE */
|
#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.
|
* All Intel64 interrupts are dynamically connected.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
|
#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, \
|
arch_irq_connect_dynamic(irq_p, priority_p, \
|
||||||
(void (*)(const void *))isr_p, \
|
(void (*)(const void *))isr_p, \
|
||||||
isr_param_p, flags_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);
|
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.
|
* @brief Return the IRQ assigned by the firmware/board to an endpoint.
|
||||||
*
|
*
|
||||||
|
|
|
@ -138,6 +138,15 @@
|
||||||
Z_ITERABLE_SECTION_ROM(dns_sd_rec, 4)
|
Z_ITERABLE_SECTION_ROM(dns_sd_rec, 4)
|
||||||
#endif
|
#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,,)
|
SECTION_DATA_PROLOGUE(log_const_sections,,)
|
||||||
{
|
{
|
||||||
__log_const_start = .;
|
__log_const_start = .;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue