irqchip/armada-370-xp: Use the generic MSI infrastructure
This commit moves the irq-armada-370-xp driver from using the PCI-specific MSI infrastructure to the generic MSI infrastructure, to which drivers are progressively converted. In this hardware, the MSI controller is directly bundled inside the interrupt controller, so we have a single Device Tree node to which multiple IRQ domaines are attached: the wired interrupt domain and the MSI interrupt domain. In order to ensure that they can be differentiated, we have to force the bus_token of the wired interrupt domain to be DOMAIN_BUS_WIRED. The MSI domain bus_token is automatically set to the appropriate value by pci_msi_create_irq_domain(). Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Suggested-by: Marc Zyngier <marc.zyngier@arm.com> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> Link: https://lkml.kernel.org/r/1455115621-22846-3-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
This commit is contained in:
Родитель
fed6d33631
Коммит
fcc392d501
|
@ -64,6 +64,7 @@ config ARMADA_370_XP_IRQ
|
||||||
bool
|
bool
|
||||||
default y if ARCH_MVEBU
|
default y if ARCH_MVEBU
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
|
select PCI_MSI_IRQ_DOMAIN if PCI_MSI
|
||||||
|
|
||||||
config ATMEL_AIC_IRQ
|
config ATMEL_AIC_IRQ
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -71,6 +71,7 @@ static u32 doorbell_mask_reg;
|
||||||
static int parent_irq;
|
static int parent_irq;
|
||||||
#ifdef CONFIG_PCI_MSI
|
#ifdef CONFIG_PCI_MSI
|
||||||
static struct irq_domain *armada_370_xp_msi_domain;
|
static struct irq_domain *armada_370_xp_msi_domain;
|
||||||
|
static struct irq_domain *armada_370_xp_msi_inner_domain;
|
||||||
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
|
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
|
||||||
static DEFINE_MUTEX(msi_used_lock);
|
static DEFINE_MUTEX(msi_used_lock);
|
||||||
static phys_addr_t msi_doorbell_addr;
|
static phys_addr_t msi_doorbell_addr;
|
||||||
|
@ -115,127 +116,99 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
|
||||||
|
|
||||||
#ifdef CONFIG_PCI_MSI
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
|
||||||
static int armada_370_xp_alloc_msi(void)
|
static struct irq_chip armada_370_xp_msi_irq_chip = {
|
||||||
|
.name = "armada_370_xp_msi_irq",
|
||||||
|
.irq_mask = pci_msi_mask_irq,
|
||||||
|
.irq_unmask = pci_msi_unmask_irq,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct msi_domain_info armada_370_xp_msi_domain_info = {
|
||||||
|
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
|
||||||
|
.chip = &armada_370_xp_msi_irq_chip,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||||
|
{
|
||||||
|
msg->address_lo = lower_32_bits(msi_doorbell_addr);
|
||||||
|
msg->address_hi = upper_32_bits(msi_doorbell_addr);
|
||||||
|
msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data,
|
||||||
|
const struct cpumask *mask, bool force)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip armada_370_xp_msi_bottom_irq_chip = {
|
||||||
|
.name = "armada_370_xp_msi_irq",
|
||||||
|
.irq_compose_msi_msg = armada_370_xp_compose_msi_msg,
|
||||||
|
.irq_set_affinity = armada_370_xp_msi_set_affinity,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
|
||||||
|
unsigned int nr_irqs, void *args)
|
||||||
{
|
{
|
||||||
int hwirq;
|
int hwirq;
|
||||||
|
|
||||||
mutex_lock(&msi_used_lock);
|
mutex_lock(&msi_used_lock);
|
||||||
hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
|
hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
|
||||||
if (hwirq >= PCI_MSI_DOORBELL_NR)
|
if (hwirq >= PCI_MSI_DOORBELL_NR) {
|
||||||
hwirq = -ENOSPC;
|
mutex_unlock(&msi_used_lock);
|
||||||
else
|
return -ENOSPC;
|
||||||
set_bit(hwirq, msi_used);
|
}
|
||||||
|
|
||||||
|
set_bit(hwirq, msi_used);
|
||||||
mutex_unlock(&msi_used_lock);
|
mutex_unlock(&msi_used_lock);
|
||||||
|
|
||||||
|
irq_domain_set_info(domain, virq, hwirq, &armada_370_xp_msi_bottom_irq_chip,
|
||||||
|
domain->host_data, handle_simple_irq,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
return hwirq;
|
return hwirq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void armada_370_xp_free_msi(int hwirq)
|
static void armada_370_xp_msi_free(struct irq_domain *domain,
|
||||||
|
unsigned int virq, unsigned int nr_irqs)
|
||||||
{
|
{
|
||||||
|
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||||
|
|
||||||
mutex_lock(&msi_used_lock);
|
mutex_lock(&msi_used_lock);
|
||||||
if (!test_bit(hwirq, msi_used))
|
if (!test_bit(d->hwirq, msi_used))
|
||||||
pr_err("trying to free unused MSI#%d\n", hwirq);
|
pr_err("trying to free unused MSI#%lu\n", d->hwirq);
|
||||||
else
|
else
|
||||||
clear_bit(hwirq, msi_used);
|
clear_bit(d->hwirq, msi_used);
|
||||||
mutex_unlock(&msi_used_lock);
|
mutex_unlock(&msi_used_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int armada_370_xp_setup_msi_irq(struct msi_controller *chip,
|
static const struct irq_domain_ops armada_370_xp_msi_domain_ops = {
|
||||||
struct pci_dev *pdev,
|
.alloc = armada_370_xp_msi_alloc,
|
||||||
struct msi_desc *desc)
|
.free = armada_370_xp_msi_free,
|
||||||
{
|
|
||||||
struct msi_msg msg;
|
|
||||||
int virq, hwirq;
|
|
||||||
|
|
||||||
/* We support MSI, but not MSI-X */
|
|
||||||
if (desc->msi_attrib.is_msix)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
hwirq = armada_370_xp_alloc_msi();
|
|
||||||
if (hwirq < 0)
|
|
||||||
return hwirq;
|
|
||||||
|
|
||||||
virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
|
|
||||||
if (!virq) {
|
|
||||||
armada_370_xp_free_msi(hwirq);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
irq_set_msi_desc(virq, desc);
|
|
||||||
|
|
||||||
msg.address_lo = msi_doorbell_addr;
|
|
||||||
msg.address_hi = 0;
|
|
||||||
msg.data = 0xf00 | (hwirq + 16);
|
|
||||||
|
|
||||||
pci_write_msi_msg(virq, &msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip,
|
|
||||||
unsigned int irq)
|
|
||||||
{
|
|
||||||
struct irq_data *d = irq_get_irq_data(irq);
|
|
||||||
unsigned long hwirq = d->hwirq;
|
|
||||||
|
|
||||||
irq_dispose_mapping(irq);
|
|
||||||
armada_370_xp_free_msi(hwirq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct irq_chip armada_370_xp_msi_irq_chip = {
|
|
||||||
.name = "armada_370_xp_msi_irq",
|
|
||||||
.irq_enable = pci_msi_unmask_irq,
|
|
||||||
.irq_disable = pci_msi_mask_irq,
|
|
||||||
.irq_mask = pci_msi_mask_irq,
|
|
||||||
.irq_unmask = pci_msi_unmask_irq,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
|
|
||||||
irq_hw_number_t hw)
|
|
||||||
{
|
|
||||||
irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
|
|
||||||
handle_simple_irq);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
|
|
||||||
.map = armada_370_xp_msi_map,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int armada_370_xp_msi_init(struct device_node *node,
|
static int armada_370_xp_msi_init(struct device_node *node,
|
||||||
phys_addr_t main_int_phys_base)
|
phys_addr_t main_int_phys_base)
|
||||||
{
|
{
|
||||||
struct msi_controller *msi_chip;
|
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int ret;
|
|
||||||
|
|
||||||
msi_doorbell_addr = main_int_phys_base +
|
msi_doorbell_addr = main_int_phys_base +
|
||||||
ARMADA_370_XP_SW_TRIG_INT_OFFS;
|
ARMADA_370_XP_SW_TRIG_INT_OFFS;
|
||||||
|
|
||||||
msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
|
armada_370_xp_msi_inner_domain =
|
||||||
if (!msi_chip)
|
irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
|
||||||
|
&armada_370_xp_msi_domain_ops, NULL);
|
||||||
|
if (!armada_370_xp_msi_inner_domain)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
|
|
||||||
msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
|
|
||||||
msi_chip->of_node = node;
|
|
||||||
|
|
||||||
armada_370_xp_msi_domain =
|
armada_370_xp_msi_domain =
|
||||||
irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
|
pci_msi_create_irq_domain(of_node_to_fwnode(node),
|
||||||
&armada_370_xp_msi_irq_ops,
|
&armada_370_xp_msi_domain_info,
|
||||||
NULL);
|
armada_370_xp_msi_inner_domain);
|
||||||
if (!armada_370_xp_msi_domain) {
|
if (!armada_370_xp_msi_domain) {
|
||||||
kfree(msi_chip);
|
irq_domain_remove(armada_370_xp_msi_inner_domain);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_pci_msi_chip_add(msi_chip);
|
|
||||||
if (ret < 0) {
|
|
||||||
irq_domain_remove(armada_370_xp_msi_domain);
|
|
||||||
kfree(msi_chip);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
|
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
|
||||||
| PCI_MSI_DOORBELL_MASK;
|
| PCI_MSI_DOORBELL_MASK;
|
||||||
|
|
||||||
|
@ -427,12 +400,12 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (is_chained) {
|
if (is_chained) {
|
||||||
irq = irq_find_mapping(armada_370_xp_msi_domain,
|
irq = irq_find_mapping(armada_370_xp_msi_inner_domain,
|
||||||
msinr - 16);
|
msinr - 16);
|
||||||
generic_handle_irq(irq);
|
generic_handle_irq(irq);
|
||||||
} else {
|
} else {
|
||||||
irq = msinr - 16;
|
irq = msinr - 16;
|
||||||
handle_domain_irq(armada_370_xp_msi_domain,
|
handle_domain_irq(armada_370_xp_msi_inner_domain,
|
||||||
irq, regs);
|
irq, regs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,8 +577,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
|
||||||
armada_370_xp_mpic_domain =
|
armada_370_xp_mpic_domain =
|
||||||
irq_domain_add_linear(node, nr_irqs,
|
irq_domain_add_linear(node, nr_irqs,
|
||||||
&armada_370_xp_mpic_irq_ops, NULL);
|
&armada_370_xp_mpic_irq_ops, NULL);
|
||||||
|
|
||||||
BUG_ON(!armada_370_xp_mpic_domain);
|
BUG_ON(!armada_370_xp_mpic_domain);
|
||||||
|
armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
|
||||||
|
|
||||||
/* Setup for the boot CPU */
|
/* Setup for the boot CPU */
|
||||||
armada_xp_mpic_perf_init();
|
armada_xp_mpic_perf_init();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче