summaryrefslogtreecommitdiff
path: root/drivers/irqchip/irq-loongson-pch-msi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip/irq-loongson-pch-msi.c')
-rw-r--r--drivers/irqchip/irq-loongson-pch-msi.c211
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