diff options
Diffstat (limited to 'drivers/irqchip/irq-loongson-pch-msi.c')
| -rw-r--r-- | drivers/irqchip/irq-loongson-pch-msi.c | 211 |
1 files changed, 126 insertions, 85 deletions
diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c index 32562b7e681b..4aedc9b90ff7 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -15,6 +15,11 @@ #include <linux/pci.h> #include <linux/slab.h> +#include <linux/irqchip/irq-msi-lib.h> +#include "irq-loongson.h" + +static int nr_pics; + struct pch_msi_data { struct mutex msi_map_lock; phys_addr_t doorbell; @@ -23,25 +28,7 @@ struct pch_msi_data { unsigned long *msi_map; }; -static void pch_msi_mask_msi_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void pch_msi_unmask_msi_irq(struct irq_data *d) -{ - irq_chip_unmask_parent(d); - pci_msi_unmask_irq(d); -} - -static struct irq_chip pch_msi_irq_chip = { - .name = "PCH PCI MSI", - .irq_mask = pch_msi_mask_msi_irq, - .irq_unmask = pch_msi_unmask_msi_irq, - .irq_ack = irq_chip_ack_parent, - .irq_set_affinity = irq_chip_set_affinity_parent, -}; +static struct fwnode_handle *pch_msi_handle[MAX_IO_PICS]; static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req) { @@ -81,12 +68,6 @@ static void pch_msi_compose_msi_msg(struct irq_data *data, msg->data = data->hwirq; } -static struct msi_domain_info pch_msi_domain_info = { - .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, - .chip = &pch_msi_irq_chip, -}; - static struct irq_chip middle_irq_chip = { .name = "PCH MSI", .irq_mask = irq_chip_mask_parent, @@ -132,7 +113,7 @@ static int pch_msi_middle_domain_alloc(struct irq_domain *domain, err_hwirq: pch_msi_free_hwirq(priv, hwirq, nr_irqs); - irq_domain_free_irqs_parent(domain, virq, i - 1); + irq_domain_free_irqs_parent(domain, virq, i); return err; } @@ -151,45 +132,90 @@ static void pch_msi_middle_domain_free(struct irq_domain *domain, static const struct irq_domain_ops pch_msi_middle_domain_ops = { .alloc = pch_msi_middle_domain_alloc, .free = pch_msi_middle_domain_free, + .select = msi_lib_irq_domain_select, }; -static int pch_msi_init_domains(struct pch_msi_data *priv, - struct device_node *node, - struct irq_domain *parent) -{ - struct irq_domain *middle_domain, *msi_domain; +#define PCH_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define PCH_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX | \ + MSI_FLAG_MULTI_PCI_MSI) + +static struct msi_parent_ops pch_msi_parent_ops = { + .required_flags = PCH_MSI_FLAGS_REQUIRED, + .supported_flags = PCH_MSI_FLAGS_SUPPORTED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, + .bus_select_mask = MATCH_PCI_MSI, + .bus_select_token = DOMAIN_BUS_NEXUS, + .prefix = "PCH-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; - middle_domain = irq_domain_create_linear(of_node_to_fwnode(node), - priv->num_irqs, - &pch_msi_middle_domain_ops, - priv); - if (!middle_domain) { +static int pch_msi_init_domains(struct pch_msi_data *priv, struct irq_domain *parent, + struct fwnode_handle *domain_handle) +{ + struct irq_domain_info info = { + .ops = &pch_msi_middle_domain_ops, + .size = priv->num_irqs, + .parent = parent, + .host_data = priv, + .fwnode = domain_handle, + }; + + if (!msi_create_parent_irq_domain(&info, &pch_msi_parent_ops)) { pr_err("Failed to create the MSI middle domain\n"); return -ENOMEM; } + return 0; +} - middle_domain->parent = parent; - irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS); +static int pch_msi_init(phys_addr_t msg_address, int irq_base, int irq_count, + struct irq_domain *parent_domain, struct fwnode_handle *domain_handle) +{ + int ret; + struct pch_msi_data *priv; - msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), - &pch_msi_domain_info, - middle_domain); - if (!msi_domain) { - pr_err("Failed to create PCI MSI domain\n"); - irq_domain_remove(middle_domain); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - } + mutex_init(&priv->msi_map_lock); + + priv->doorbell = msg_address; + priv->irq_first = irq_base; + priv->num_irqs = irq_count; + + priv->msi_map = bitmap_zalloc(priv->num_irqs, GFP_KERNEL); + if (!priv->msi_map) + goto err_priv; + + pr_debug("Registering %d MSIs, starting at %d\n", + priv->num_irqs, priv->irq_first); + + ret = pch_msi_init_domains(priv, parent_domain, domain_handle); + if (ret) + goto err_map; + + pch_msi_handle[nr_pics++] = domain_handle; return 0; + +err_map: + bitmap_free(priv->msi_map); +err_priv: + kfree(priv); + + return -EINVAL; } -static int pch_msi_init(struct device_node *node, - struct device_node *parent) +#ifdef CONFIG_OF +static int pch_msi_of_init(struct device_node *node, struct device_node *parent) { - struct pch_msi_data *priv; - struct irq_domain *parent_domain; + int err; + int irq_base, irq_count; struct resource res; - int ret; + struct irq_domain *parent_domain; parent_domain = irq_find_host(parent); if (!parent_domain) { @@ -197,54 +223,69 @@ static int pch_msi_init(struct device_node *node, return -ENXIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - mutex_init(&priv->msi_map_lock); - - ret = of_address_to_resource(node, 0, &res); - if (ret) { + if (of_address_to_resource(node, 0, &res)) { pr_err("Failed to allocate resource\n"); - goto err_priv; + return -EINVAL; } - priv->doorbell = res.start; - - if (of_property_read_u32(node, "loongson,msi-base-vec", - &priv->irq_first)) { + if (of_property_read_u32(node, "loongson,msi-base-vec", &irq_base)) { pr_err("Unable to parse MSI vec base\n"); - ret = -EINVAL; - goto err_priv; + return -EINVAL; } - if (of_property_read_u32(node, "loongson,msi-num-vecs", - &priv->num_irqs)) { + if (of_property_read_u32(node, "loongson,msi-num-vecs", &irq_count)) { pr_err("Unable to parse MSI vec number\n"); - ret = -EINVAL; - goto err_priv; + return -EINVAL; } - priv->msi_map = bitmap_zalloc(priv->num_irqs, GFP_KERNEL); - if (!priv->msi_map) { - ret = -ENOMEM; - goto err_priv; - } + err = pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_fwnode_handle(node)); + if (err < 0) + return err; - pr_debug("Registering %d MSIs, starting at %d\n", - priv->num_irqs, priv->irq_first); + return 0; +} - ret = pch_msi_init_domains(priv, node, parent_domain); - if (ret) - goto err_map; +IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_of_init); +#endif - return 0; +#ifdef CONFIG_ACPI +struct fwnode_handle *get_pch_msi_handle(int pci_segment) +{ + if (cpu_has_avecint) + return pch_msi_handle[0]; + + for (int i = 0; i < MAX_IO_PICS; i++) { + if (msi_group[i].pci_segment == pci_segment) + return pch_msi_handle[i]; + } + return pch_msi_handle[0]; +} + +int __init pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_msi_pic *acpi_pchmsi) +{ + int ret; + struct fwnode_handle *domain_handle; + + domain_handle = irq_domain_alloc_fwnode(&acpi_pchmsi->msg_address); + ret = pch_msi_init(acpi_pchmsi->msg_address, acpi_pchmsi->start, + acpi_pchmsi->count, parent, domain_handle); + if (ret < 0) + irq_domain_free_fwnode(domain_handle); -err_map: - kfree(priv->msi_map); -err_priv: - kfree(priv); return ret; } -IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init); +int __init pch_msi_acpi_init_avec(struct irq_domain *parent) +{ + if (pch_msi_handle[0]) + return 0; + + pch_msi_handle[0] = parent->fwnode; + irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS); + + parent->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + parent->msi_parent_ops = &pch_msi_parent_ops; + + return 0; +} +#endif |
