123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */
- #include <linux/interrupt.h>
- #include <linux/iommu.h>
- #include <linux/ipc_logging.h>
- #include <linux/irqchip/chained_irq.h>
- #include <linux/irqdomain.h>
- #include <linux/msi.h>
- #include <linux/msm_pcie.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #include <linux/of_pci.h>
- #include <linux/pci.h>
- #include <linux/platform_device.h>
- #define PCIE_MSI_CTRL_BASE (0x820)
- #define PCIE_MSI_CTRL_ADDR_OFFS (PCIE_MSI_CTRL_BASE)
- #define PCIE_MSI_CTRL_UPPER_ADDR_OFFS (PCIE_MSI_CTRL_BASE + 0x4)
- #define PCIE_MSI_CTRL_INT_N_EN_OFFS(n) (PCIE_MSI_CTRL_BASE + 0x8 + 0xc * (n))
- #define PCIE_MSI_CTRL_INT_N_MASK_OFFS(n) (PCIE_MSI_CTRL_BASE + 0xc + 0xc * (n))
- #define PCIE_MSI_CTRL_INT_N_STATUS_OFFS(n) \
- (PCIE_MSI_CTRL_BASE + 0x10 + 0xc * (n))
- #define MSI_IRQ_PER_GRP (32)
- enum msi_type {
- MSM_MSI_TYPE_QCOM,
- MSM_MSI_TYPE_SNPS,
- };
- struct msm_msi_irq {
- struct msm_msi_client *client;
- struct msm_msi_grp *grp; /* group the irq belongs to */
- u32 grp_index; /* index in the group */
- unsigned int hwirq; /* MSI controller hwirq */
- unsigned int virq; /* MSI controller virq */
- u32 pos; /* position in MSI bitmap */
- };
- struct msm_msi_grp {
- /* registers for SNPS only */
- void __iomem *int_en_reg;
- void __iomem *int_mask_reg;
- void __iomem *int_status_reg;
- u32 mask; /* tracks masked/unmasked MSI */
- struct msm_msi_irq irqs[MSI_IRQ_PER_GRP];
- };
- struct msm_msi {
- struct list_head clients;
- struct device *dev;
- struct device_node *of_node;
- int nr_hwirqs;
- int nr_virqs;
- int nr_grps;
- struct msm_msi_grp *grps;
- unsigned long *bitmap; /* tracks used/unused MSI */
- struct mutex mutex; /* mutex for modifying MSI client list and bitmap */
- struct irq_domain *inner_domain; /* parent domain; gen irq related */
- struct irq_domain *msi_domain; /* child domain; pci related */
- phys_addr_t msi_addr;
- u32 msi_addr_size;
- enum msi_type type;
- spinlock_t cfg_lock; /* lock for configuring Synopsys MSI registers */
- bool cfg_access; /* control access to MSI registers */
- void __iomem *pcie_cfg;
- void (*mask_irq)(struct irq_data *data);
- void (*unmask_irq)(struct irq_data *data);
- };
- /* structure for each client of MSI controller */
- struct msm_msi_client {
- struct list_head node;
- struct msm_msi *msi;
- struct device *dev; /* client's dev of pci_dev */
- u32 nr_irqs; /* nr_irqs allocated for client */
- dma_addr_t msi_addr;
- };
- static void msm_msi_snps_handler(struct irq_desc *desc)
- {
- struct irq_chip *chip = irq_desc_get_chip(desc);
- struct msm_msi_grp *msi_grp;
- int i;
- u32 status, mask;
- chained_irq_enter(chip, desc);
- msi_grp = irq_desc_get_handler_data(desc);
- status = readl_relaxed(msi_grp->int_status_reg);
- /* always update the mask set in msm_msi_snps_mask_irq */
- mask = msi_grp->mask;
- writel_relaxed(mask, msi_grp->int_mask_reg);
- /* process only interrupts which are not masked */
- status ^= (status & mask);
- writel_relaxed(status, msi_grp->int_status_reg);
- for (i = 0; status; i++, status >>= 1)
- if (status & 0x1)
- generic_handle_irq(msi_grp->irqs[i].virq);
- chained_irq_exit(chip, desc);
- }
- static void msm_msi_qgic_handler(struct irq_desc *desc)
- {
- struct irq_chip *chip = irq_desc_get_chip(desc);
- struct msm_msi *msi;
- unsigned int virq;
- chained_irq_enter(chip, desc);
- msi = irq_desc_get_handler_data(desc);
- virq = irq_find_mapping(msi->inner_domain, irq_desc_get_irq(desc));
- generic_handle_irq(virq);
- chained_irq_exit(chip, desc);
- }
- static void msm_msi_snps_mask_irq(struct irq_data *data)
- {
- struct msm_msi_irq *msi_irq = irq_data_get_irq_chip_data(data);
- struct msm_msi_grp *msi_grp = msi_irq->grp;
- struct msm_msi *msi = msi_irq->client->msi;
- unsigned long flags;
- spin_lock_irqsave(&msi->cfg_lock, flags);
- msi_grp->mask |= BIT(msi_irq->grp_index);
- spin_unlock_irqrestore(&msi->cfg_lock, flags);
- }
- static void msm_msi_qgic_mask_irq(struct irq_data *data)
- {
- struct irq_data *parent_data;
- parent_data = irq_get_irq_data(irqd_to_hwirq(data));
- if (!parent_data || !parent_data->chip)
- return;
- parent_data->chip->irq_mask(parent_data);
- }
- static void msm_msi_mask_irq(struct irq_data *data)
- {
- struct irq_data *parent_data;
- struct msm_msi_irq *msi_irq;
- struct msm_msi *msi;
- unsigned long flags;
- parent_data = data->parent_data;
- if (!parent_data)
- return;
- msi_irq = irq_data_get_irq_chip_data(parent_data);
- msi = msi_irq->client->msi;
- spin_lock_irqsave(&msi->cfg_lock, flags);
- if (msi->cfg_access)
- pci_msi_mask_irq(data);
- spin_unlock_irqrestore(&msi->cfg_lock, flags);
- msi->mask_irq(parent_data);
- }
- static void msm_msi_snps_unmask_irq(struct irq_data *data)
- {
- struct msm_msi_irq *msi_irq = irq_data_get_irq_chip_data(data);
- struct msm_msi_grp *msi_grp = msi_irq->grp;
- struct msm_msi *msi = msi_irq->client->msi;
- unsigned long flags;
- spin_lock_irqsave(&msi->cfg_lock, flags);
- msi_grp->mask &= ~BIT(msi_irq->grp_index);
- if (msi->cfg_access)
- writel_relaxed(msi_grp->mask, msi_grp->int_mask_reg);
- spin_unlock_irqrestore(&msi->cfg_lock, flags);
- }
- static void msm_msi_qgic_unmask_irq(struct irq_data *data)
- {
- struct irq_data *parent_data;
- parent_data = irq_get_irq_data(irqd_to_hwirq(data));
- if (!parent_data || !parent_data->chip)
- return;
- parent_data->chip->irq_unmask(parent_data);
- }
- static void msm_msi_unmask_irq(struct irq_data *data)
- {
- struct irq_data *parent_data;
- struct msm_msi_irq *msi_irq;
- struct msm_msi *msi;
- unsigned long flags;
- parent_data = data->parent_data;
- if (!parent_data)
- return;
- msi_irq = irq_data_get_irq_chip_data(parent_data);
- msi = msi_irq->client->msi;
- msi->unmask_irq(parent_data);
- spin_lock_irqsave(&msi->cfg_lock, flags);
- if (msi->cfg_access)
- pci_msi_unmask_irq(data);
- spin_unlock_irqrestore(&msi->cfg_lock, flags);
- }
- static struct irq_chip msm_msi_irq_chip = {
- .name = "gic_msm_pci_msi",
- .irq_enable = msm_msi_unmask_irq,
- .irq_disable = msm_msi_mask_irq,
- .irq_mask = msm_msi_mask_irq,
- .irq_unmask = msm_msi_unmask_irq,
- };
- static int msm_msi_domain_prepare(struct irq_domain *domain, struct device *dev,
- int nvec, msi_alloc_info_t *arg)
- {
- struct msm_msi *msi = domain->parent->host_data;
- struct msm_msi_client *client;
- client = kzalloc(sizeof(*client), GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- client->msi = msi;
- client->dev = dev;
- client->msi_addr = msi->msi_addr;
- /*
- * Accesses to QGIC MSI doorbell register goes through PCIe SMMU and
- * needs to be mapped. Synopsys MSI doorbell is within the PCIe core
- * and does not need to be mapped.
- */
- if (msi->type == MSM_MSI_TYPE_QCOM) {
- client->msi_addr = dma_map_resource(client->dev, msi->msi_addr,
- msi->msi_addr_size, DMA_FROM_DEVICE, 0);
- if (dma_mapping_error(client->dev, client->msi_addr)) {
- dev_err(msi->dev, "MSI: failed to map msi address\n");
- client->msi_addr = 0;
- kfree(client);
- return -ENOMEM;
- }
- }
- mutex_lock(&msi->mutex);
- list_add_tail(&client->node, &msi->clients);
- mutex_unlock(&msi->mutex);
- /* zero out struct for framework */
- memset(arg, 0, sizeof(*arg));
- return 0;
- }
- static struct msi_domain_ops msm_msi_domain_ops = {
- .msi_prepare = msm_msi_domain_prepare,
- };
- static struct msi_domain_info msm_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,
- .ops = &msm_msi_domain_ops,
- .chip = &msm_msi_irq_chip,
- };
- static int msm_msi_irq_set_affinity(struct irq_data *data,
- const struct cpumask *mask, bool force)
- {
- struct irq_data *parent_data = irq_get_irq_data(irqd_to_hwirq(data));
- if (!parent_data)
- return -ENODEV;
- /* set affinity for MSM MSI HW IRQ */
- if (parent_data->chip->irq_set_affinity)
- return parent_data->chip->irq_set_affinity(parent_data,
- mask, force);
- return -EINVAL;
- }
- static void msm_msi_irq_compose_msi_msg(struct irq_data *data,
- struct msi_msg *msg)
- {
- struct msm_msi_irq *msi_irq = irq_data_get_irq_chip_data(data);
- struct irq_data *parent_data = irq_get_irq_data(irqd_to_hwirq(data));
- struct msm_msi_client *client = msi_irq->client;
- struct msm_msi *msi = client->msi;
- if (!parent_data)
- return;
- msg->address_lo = lower_32_bits(client->msi_addr);
- msg->address_hi = upper_32_bits(client->msi_addr);
- msg->data = (msi->type == MSM_MSI_TYPE_QCOM) ?
- irqd_to_hwirq(parent_data) : msi_irq->pos;
- }
- static struct irq_chip msm_msi_bottom_irq_chip = {
- .name = "msm_msi",
- .irq_set_affinity = msm_msi_irq_set_affinity,
- .irq_compose_msi_msg = msm_msi_irq_compose_msi_msg,
- };
- static int msm_msi_irq_domain_alloc(struct irq_domain *domain,
- unsigned int virq, unsigned int nr_irqs,
- void *args)
- {
- struct msm_msi *msi = domain->host_data;
- struct msm_msi_client *tmp, *client = NULL;
- struct device *dev = ((msi_alloc_info_t *)args)->desc->dev;
- int i, ret = 0;
- int pos;
- mutex_lock(&msi->mutex);
- list_for_each_entry(tmp, &msi->clients, node) {
- if (tmp->dev == dev) {
- client = tmp;
- break;
- }
- }
- if (!client) {
- dev_err(msi->dev, "MSI: failed to find client dev\n");
- ret = -ENODEV;
- goto out;
- }
- pos = bitmap_find_next_zero_area(msi->bitmap, msi->nr_virqs, 0,
- nr_irqs, nr_irqs - 1);
- if (pos < msi->nr_virqs) {
- bitmap_set(msi->bitmap, pos, nr_irqs);
- } else {
- ret = -ENOSPC;
- goto out;
- }
- for (i = 0; i < nr_irqs; i++) {
- u32 grp = pos / MSI_IRQ_PER_GRP;
- u32 index = pos % MSI_IRQ_PER_GRP;
- struct msm_msi_irq *msi_irq = &msi->grps[grp].irqs[index];
- msi_irq->virq = virq + i;
- msi_irq->client = client;
- irq_domain_set_info(domain, msi_irq->virq,
- msi_irq->hwirq,
- &msm_msi_bottom_irq_chip, msi_irq,
- handle_simple_irq, NULL, NULL);
- client->nr_irqs++;
- pos++;
- }
- out:
- mutex_unlock(&msi->mutex);
- return ret;
- }
- static void msm_msi_irq_domain_free(struct irq_domain *domain,
- unsigned int virq, unsigned int nr_irqs)
- {
- struct irq_data *data = irq_domain_get_irq_data(domain, virq);
- struct msm_msi_irq *msi_irq;
- struct msm_msi_client *client;
- struct msm_msi *msi;
- if (!data)
- return;
- msi_irq = irq_data_get_irq_chip_data(data);
- client = msi_irq->client;
- msi = client->msi;
- mutex_lock(&msi->mutex);
- bitmap_clear(msi->bitmap, msi_irq->pos, nr_irqs);
- client->nr_irqs -= nr_irqs;
- if (!client->nr_irqs) {
- if (msi->type == MSM_MSI_TYPE_QCOM)
- dma_unmap_resource(client->dev, client->msi_addr,
- PAGE_SIZE, DMA_FROM_DEVICE, 0);
- list_del(&client->node);
- kfree(client);
- }
- mutex_unlock(&msi->mutex);
- irq_domain_free_irqs_parent(domain, virq, nr_irqs);
- }
- static const struct irq_domain_ops msi_domain_ops = {
- .alloc = msm_msi_irq_domain_alloc,
- .free = msm_msi_irq_domain_free,
- };
- static int msm_msi_alloc_domains(struct msm_msi *msi)
- {
- msi->inner_domain = irq_domain_add_linear(NULL, msi->nr_virqs,
- &msi_domain_ops, msi);
- if (!msi->inner_domain) {
- dev_err(msi->dev, "MSI: failed to create IRQ domain\n");
- return -ENOMEM;
- }
- msi->msi_domain = pci_msi_create_irq_domain(
- of_node_to_fwnode(msi->of_node),
- &msm_msi_domain_info,
- msi->inner_domain);
- if (!msi->msi_domain) {
- dev_err(msi->dev, "MSI: failed to create MSI domain\n");
- irq_domain_remove(msi->inner_domain);
- return -ENOMEM;
- }
- return 0;
- }
- static int msm_msi_snps_irq_setup(struct msm_msi *msi)
- {
- int i, index, ret;
- struct msm_msi_grp *msi_grp;
- struct msm_msi_irq *msi_irq;
- unsigned int irq = 0;
- /* setup each MSI group. nr_hwirqs == nr_grps */
- for (i = 0; i < msi->nr_hwirqs; i++) {
- irq = irq_of_parse_and_map(msi->of_node, i);
- if (!irq) {
- dev_err(msi->dev,
- "MSI: failed to parse/map interrupt\n");
- ret = -ENODEV;
- goto free_irqs;
- }
- ret = enable_irq_wake(irq);
- if (ret) {
- dev_err(msi->dev,
- "MSI: Unable to set enable_irq_wake for interrupt: %d: %d\n",
- i, irq);
- goto free_irq;
- }
- msi_grp = &msi->grps[i];
- msi_grp->int_en_reg = msi->pcie_cfg +
- PCIE_MSI_CTRL_INT_N_EN_OFFS(i);
- msi_grp->int_mask_reg = msi->pcie_cfg +
- PCIE_MSI_CTRL_INT_N_MASK_OFFS(i);
- msi_grp->int_status_reg = msi->pcie_cfg +
- PCIE_MSI_CTRL_INT_N_STATUS_OFFS(i);
- for (index = 0; index < MSI_IRQ_PER_GRP; index++) {
- msi_irq = &msi_grp->irqs[index];
- msi_irq->grp = msi_grp;
- msi_irq->grp_index = index;
- msi_irq->pos = (i * MSI_IRQ_PER_GRP) + index;
- msi_irq->hwirq = irq;
- }
- irq_set_chained_handler_and_data(irq, msm_msi_snps_handler,
- msi_grp);
- }
- return 0;
- free_irq:
- irq_dispose_mapping(irq);
- free_irqs:
- for (--i; i >= 0; i--) {
- irq = msi->grps[i].irqs[0].hwirq;
- irq_set_chained_handler_and_data(irq, NULL, NULL);
- disable_irq_wake(irq);
- irq_dispose_mapping(irq);
- }
- return ret;
- }
- static int msm_msi_qgic_irq_setup(struct msm_msi *msi)
- {
- int i, ret;
- u32 index, grp;
- struct msm_msi_grp *msi_grp;
- struct msm_msi_irq *msi_irq;
- unsigned int irq = 0;
- for (i = 0; i < msi->nr_hwirqs; i++) {
- irq = irq_of_parse_and_map(msi->of_node, i);
- if (!irq) {
- dev_err(msi->dev,
- "MSI: failed to parse/map interrupt\n");
- ret = -ENODEV;
- goto free_irqs;
- }
- ret = enable_irq_wake(irq);
- if (ret) {
- dev_err(msi->dev,
- "MSI: Unable to set enable_irq_wake for interrupt: %d: %d\n",
- i, irq);
- goto free_irq;
- }
- grp = i / MSI_IRQ_PER_GRP;
- index = i % MSI_IRQ_PER_GRP;
- msi_grp = &msi->grps[grp];
- msi_irq = &msi_grp->irqs[index];
- msi_irq->grp = msi_grp;
- msi_irq->grp_index = index;
- msi_irq->pos = i;
- msi_irq->hwirq = irq;
- irq_set_chained_handler_and_data(irq, msm_msi_qgic_handler,
- msi);
- }
- return 0;
- free_irq:
- irq_dispose_mapping(irq);
- free_irqs:
- for (--i; i >= 0; i--) {
- grp = i / MSI_IRQ_PER_GRP;
- index = i % MSI_IRQ_PER_GRP;
- irq = msi->grps[grp].irqs[index].hwirq;
- irq_set_chained_handler_and_data(irq, NULL, NULL);
- disable_irq_wake(irq);
- irq_dispose_mapping(irq);
- }
- return ret;
- }
- /* control access to PCIe MSI registers */
- void msm_msi_config_access(struct irq_domain *domain, bool allow)
- {
- struct msm_msi *msi = domain->parent->host_data;
- unsigned long flags;
- spin_lock_irqsave(&msi->cfg_lock, flags);
- msi->cfg_access = allow;
- spin_unlock_irqrestore(&msi->cfg_lock, flags);
- }
- EXPORT_SYMBOL(msm_msi_config_access);
- void msm_msi_config(struct irq_domain *domain)
- {
- struct msm_msi *msi;
- int i;
- msi = domain->parent->host_data;
- /* PCIe core driver sets to false during LPM */
- msm_msi_config_access(domain, true);
- if (msi->type == MSM_MSI_TYPE_QCOM)
- return;
- /* program Synopsys MSI termination address */
- writel_relaxed(msi->msi_addr, msi->pcie_cfg + PCIE_MSI_CTRL_ADDR_OFFS);
- writel_relaxed(0, msi->pcie_cfg + PCIE_MSI_CTRL_UPPER_ADDR_OFFS);
- /* restore mask and enable all interrupts for each group */
- for (i = 0; i < msi->nr_grps; i++) {
- struct msm_msi_grp *msi_grp = &msi->grps[i];
- writel_relaxed(msi_grp->mask, msi_grp->int_mask_reg);
- writel_relaxed(~0, msi_grp->int_en_reg);
- }
- }
- EXPORT_SYMBOL(msm_msi_config);
- int msm_msi_init(struct device *dev)
- {
- int ret;
- struct msm_msi *msi;
- struct device_node *of_node;
- const __be32 *prop_val;
- struct of_phandle_args irq;
- u32 size_exp = 0;
- struct resource *res;
- int (*msi_irq_setup)(struct msm_msi *msi);
- if (!dev->of_node) {
- dev_err(dev, "MSI: missing DT node\n");
- return -EINVAL;
- }
- of_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
- if (!of_node) {
- dev_err(dev, "MSI: no phandle for MSI found\n");
- return -ENODEV;
- }
- if (!of_device_is_compatible(of_node, "qcom,pci-msi")) {
- dev_err(dev, "MSI: no compatible qcom,pci-msi found\n");
- ret = -ENODEV;
- goto err;
- }
- if (!of_find_property(of_node, "msi-controller", NULL)) {
- ret = -ENODEV;
- goto err;
- }
- msi = kzalloc(sizeof(*msi), GFP_KERNEL);
- if (!msi) {
- ret = -ENOMEM;
- goto err;
- }
- msi->dev = dev;
- msi->of_node = of_node;
- mutex_init(&msi->mutex);
- spin_lock_init(&msi->cfg_lock);
- INIT_LIST_HEAD(&msi->clients);
- prop_val = of_get_address(msi->of_node, 0, NULL, NULL);
- if (!prop_val) {
- dev_err(msi->dev, "MSI: missing 'reg' devicetree\n");
- ret = -EINVAL;
- goto err;
- }
- msi->msi_addr = be32_to_cpup(prop_val);
- if (!msi->msi_addr) {
- dev_err(msi->dev, "MSI: failed to get MSI address\n");
- ret = -EINVAL;
- goto err;
- }
- of_property_read_u32(of_node, "qcom,msi-addr-size-exp", &size_exp);
- size_exp = (size_exp > PAGE_SHIFT) ? size_exp : PAGE_SHIFT;
- msi->msi_addr_size = 1 << size_exp;
- msi->type = of_property_read_bool(msi->of_node, "qcom,snps") ?
- MSM_MSI_TYPE_SNPS : MSM_MSI_TYPE_QCOM;
- dev_info(msi->dev, "MSI: %s controller is present\n",
- msi->type == MSM_MSI_TYPE_SNPS ? "synopsys" : "qgic");
- while (of_irq_parse_one(msi->of_node, msi->nr_hwirqs, &irq) == 0)
- msi->nr_hwirqs++;
- if (!msi->nr_hwirqs) {
- dev_err(msi->dev, "MSI: found no MSI interrupts\n");
- ret = -ENODEV;
- goto err;
- }
- if (msi->type == MSM_MSI_TYPE_SNPS) {
- res = platform_get_resource_byname(to_platform_device(dev),
- IORESOURCE_MEM, "dm_core");
- if (!res) {
- dev_err(msi->dev,
- "MSI: failed to get PCIe register base\n");
- ret = -ENODEV;
- goto err;
- }
- msi->pcie_cfg = ioremap(res->start, resource_size(res));
- if (!msi->pcie_cfg) {
- ret = -ENOMEM;
- goto free_msi;
- }
- msi->nr_virqs = msi->nr_hwirqs * MSI_IRQ_PER_GRP;
- msi->nr_grps = msi->nr_hwirqs;
- msi->mask_irq = msm_msi_snps_mask_irq;
- msi->unmask_irq = msm_msi_snps_unmask_irq;
- msi_irq_setup = msm_msi_snps_irq_setup;
- } else {
- msi->nr_virqs = msi->nr_hwirqs;
- msi->nr_grps = 1;
- msi->mask_irq = msm_msi_qgic_mask_irq;
- msi->unmask_irq = msm_msi_qgic_unmask_irq;
- msi_irq_setup = msm_msi_qgic_irq_setup;
- }
- msi->grps = kcalloc(msi->nr_grps, sizeof(*msi->grps), GFP_KERNEL);
- if (!msi->grps) {
- ret = -ENOMEM;
- goto unmap_cfg;
- }
- msi->bitmap = kcalloc(BITS_TO_LONGS(msi->nr_virqs),
- sizeof(*msi->bitmap), GFP_KERNEL);
- if (!msi->bitmap) {
- ret = -ENOMEM;
- goto free_grps;
- }
- ret = msm_msi_alloc_domains(msi);
- if (ret) {
- dev_err(msi->dev, "MSI: failed to allocate MSI domains\n");
- goto free_bitmap;
- }
- ret = msi_irq_setup(msi);
- if (ret)
- goto remove_domains;
- msm_msi_config(msi->msi_domain);
- return 0;
- remove_domains:
- irq_domain_remove(msi->msi_domain);
- irq_domain_remove(msi->inner_domain);
- free_bitmap:
- kfree(msi->bitmap);
- free_grps:
- kfree(msi->grps);
- unmap_cfg:
- iounmap(msi->pcie_cfg);
- free_msi:
- kfree(msi);
- err:
- of_node_put(of_node);
- return ret;
- }
- EXPORT_SYMBOL(msm_msi_init);
|