Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #define pr_fmt(fmt) "mvebu-sei: " fmt
0004 
0005 #include <linux/interrupt.h>
0006 #include <linux/irq.h>
0007 #include <linux/irqchip.h>
0008 #include <linux/irqchip/chained_irq.h>
0009 #include <linux/irqdomain.h>
0010 #include <linux/kernel.h>
0011 #include <linux/msi.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/of_platform.h>
0016 
0017 /* Cause register */
0018 #define GICP_SECR(idx)      (0x0  + ((idx) * 0x4))
0019 /* Mask register */
0020 #define GICP_SEMR(idx)      (0x20 + ((idx) * 0x4))
0021 #define GICP_SET_SEI_OFFSET 0x30
0022 
0023 #define SEI_IRQ_COUNT_PER_REG   32
0024 #define SEI_IRQ_REG_COUNT   2
0025 #define SEI_IRQ_COUNT       (SEI_IRQ_COUNT_PER_REG * SEI_IRQ_REG_COUNT)
0026 #define SEI_IRQ_REG_IDX(irq_id) ((irq_id) / SEI_IRQ_COUNT_PER_REG)
0027 #define SEI_IRQ_REG_BIT(irq_id) ((irq_id) % SEI_IRQ_COUNT_PER_REG)
0028 
0029 struct mvebu_sei_interrupt_range {
0030     u32 first;
0031     u32 size;
0032 };
0033 
0034 struct mvebu_sei_caps {
0035     struct mvebu_sei_interrupt_range ap_range;
0036     struct mvebu_sei_interrupt_range cp_range;
0037 };
0038 
0039 struct mvebu_sei {
0040     struct device *dev;
0041     void __iomem *base;
0042     struct resource *res;
0043     struct irq_domain *sei_domain;
0044     struct irq_domain *ap_domain;
0045     struct irq_domain *cp_domain;
0046     const struct mvebu_sei_caps *caps;
0047 
0048     /* Lock on MSI allocations/releases */
0049     struct mutex cp_msi_lock;
0050     DECLARE_BITMAP(cp_msi_bitmap, SEI_IRQ_COUNT);
0051 
0052     /* Lock on IRQ masking register */
0053     raw_spinlock_t mask_lock;
0054 };
0055 
0056 static void mvebu_sei_ack_irq(struct irq_data *d)
0057 {
0058     struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
0059     u32 reg_idx = SEI_IRQ_REG_IDX(d->hwirq);
0060 
0061     writel_relaxed(BIT(SEI_IRQ_REG_BIT(d->hwirq)),
0062                sei->base + GICP_SECR(reg_idx));
0063 }
0064 
0065 static void mvebu_sei_mask_irq(struct irq_data *d)
0066 {
0067     struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
0068     u32 reg, reg_idx = SEI_IRQ_REG_IDX(d->hwirq);
0069     unsigned long flags;
0070 
0071     /* 1 disables the interrupt */
0072     raw_spin_lock_irqsave(&sei->mask_lock, flags);
0073     reg = readl_relaxed(sei->base + GICP_SEMR(reg_idx));
0074     reg |= BIT(SEI_IRQ_REG_BIT(d->hwirq));
0075     writel_relaxed(reg, sei->base + GICP_SEMR(reg_idx));
0076     raw_spin_unlock_irqrestore(&sei->mask_lock, flags);
0077 }
0078 
0079 static void mvebu_sei_unmask_irq(struct irq_data *d)
0080 {
0081     struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
0082     u32 reg, reg_idx = SEI_IRQ_REG_IDX(d->hwirq);
0083     unsigned long flags;
0084 
0085     /* 0 enables the interrupt */
0086     raw_spin_lock_irqsave(&sei->mask_lock, flags);
0087     reg = readl_relaxed(sei->base + GICP_SEMR(reg_idx));
0088     reg &= ~BIT(SEI_IRQ_REG_BIT(d->hwirq));
0089     writel_relaxed(reg, sei->base + GICP_SEMR(reg_idx));
0090     raw_spin_unlock_irqrestore(&sei->mask_lock, flags);
0091 }
0092 
0093 static int mvebu_sei_set_affinity(struct irq_data *d,
0094                   const struct cpumask *mask_val,
0095                   bool force)
0096 {
0097     return -EINVAL;
0098 }
0099 
0100 static int mvebu_sei_set_irqchip_state(struct irq_data *d,
0101                        enum irqchip_irq_state which,
0102                        bool state)
0103 {
0104     /* We can only clear the pending state by acking the interrupt */
0105     if (which != IRQCHIP_STATE_PENDING || state)
0106         return -EINVAL;
0107 
0108     mvebu_sei_ack_irq(d);
0109     return 0;
0110 }
0111 
0112 static struct irq_chip mvebu_sei_irq_chip = {
0113     .name           = "SEI",
0114     .irq_ack        = mvebu_sei_ack_irq,
0115     .irq_mask       = mvebu_sei_mask_irq,
0116     .irq_unmask     = mvebu_sei_unmask_irq,
0117     .irq_set_affinity       = mvebu_sei_set_affinity,
0118     .irq_set_irqchip_state  = mvebu_sei_set_irqchip_state,
0119 };
0120 
0121 static int mvebu_sei_ap_set_type(struct irq_data *data, unsigned int type)
0122 {
0123     if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH)
0124         return -EINVAL;
0125 
0126     return 0;
0127 }
0128 
0129 static struct irq_chip mvebu_sei_ap_irq_chip = {
0130     .name           = "AP SEI",
0131     .irq_ack        = irq_chip_ack_parent,
0132     .irq_mask       = irq_chip_mask_parent,
0133     .irq_unmask     = irq_chip_unmask_parent,
0134     .irq_set_affinity       = irq_chip_set_affinity_parent,
0135     .irq_set_type       = mvebu_sei_ap_set_type,
0136 };
0137 
0138 static void mvebu_sei_cp_compose_msi_msg(struct irq_data *data,
0139                      struct msi_msg *msg)
0140 {
0141     struct mvebu_sei *sei = data->chip_data;
0142     phys_addr_t set = sei->res->start + GICP_SET_SEI_OFFSET;
0143 
0144     msg->data = data->hwirq + sei->caps->cp_range.first;
0145     msg->address_lo = lower_32_bits(set);
0146     msg->address_hi = upper_32_bits(set);
0147 }
0148 
0149 static int mvebu_sei_cp_set_type(struct irq_data *data, unsigned int type)
0150 {
0151     if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)
0152         return -EINVAL;
0153 
0154     return 0;
0155 }
0156 
0157 static struct irq_chip mvebu_sei_cp_irq_chip = {
0158     .name           = "CP SEI",
0159     .irq_ack        = irq_chip_ack_parent,
0160     .irq_mask       = irq_chip_mask_parent,
0161     .irq_unmask     = irq_chip_unmask_parent,
0162     .irq_set_affinity       = irq_chip_set_affinity_parent,
0163     .irq_set_type       = mvebu_sei_cp_set_type,
0164     .irq_compose_msi_msg    = mvebu_sei_cp_compose_msi_msg,
0165 };
0166 
0167 static int mvebu_sei_domain_alloc(struct irq_domain *domain, unsigned int virq,
0168                   unsigned int nr_irqs, void *arg)
0169 {
0170     struct mvebu_sei *sei = domain->host_data;
0171     struct irq_fwspec *fwspec = arg;
0172 
0173     /* Not much to do, just setup the irqdata */
0174     irq_domain_set_hwirq_and_chip(domain, virq, fwspec->param[0],
0175                       &mvebu_sei_irq_chip, sei);
0176 
0177     return 0;
0178 }
0179 
0180 static void mvebu_sei_domain_free(struct irq_domain *domain, unsigned int virq,
0181                   unsigned int nr_irqs)
0182 {
0183     int i;
0184 
0185     for (i = 0; i < nr_irqs; i++) {
0186         struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
0187         irq_set_handler(virq + i, NULL);
0188         irq_domain_reset_irq_data(d);
0189     }
0190 }
0191 
0192 static const struct irq_domain_ops mvebu_sei_domain_ops = {
0193     .alloc  = mvebu_sei_domain_alloc,
0194     .free   = mvebu_sei_domain_free,
0195 };
0196 
0197 static int mvebu_sei_ap_translate(struct irq_domain *domain,
0198                   struct irq_fwspec *fwspec,
0199                   unsigned long *hwirq,
0200                   unsigned int *type)
0201 {
0202     *hwirq = fwspec->param[0];
0203     *type  = IRQ_TYPE_LEVEL_HIGH;
0204 
0205     return 0;
0206 }
0207 
0208 static int mvebu_sei_ap_alloc(struct irq_domain *domain, unsigned int virq,
0209                   unsigned int nr_irqs, void *arg)
0210 {
0211     struct mvebu_sei *sei = domain->host_data;
0212     struct irq_fwspec fwspec;
0213     unsigned long hwirq;
0214     unsigned int type;
0215     int err;
0216 
0217     mvebu_sei_ap_translate(domain, arg, &hwirq, &type);
0218 
0219     fwspec.fwnode = domain->parent->fwnode;
0220     fwspec.param_count = 1;
0221     fwspec.param[0] = hwirq + sei->caps->ap_range.first;
0222 
0223     err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
0224     if (err)
0225         return err;
0226 
0227     irq_domain_set_info(domain, virq, hwirq,
0228                 &mvebu_sei_ap_irq_chip, sei,
0229                 handle_level_irq, NULL, NULL);
0230     irq_set_probe(virq);
0231 
0232     return 0;
0233 }
0234 
0235 static const struct irq_domain_ops mvebu_sei_ap_domain_ops = {
0236     .translate  = mvebu_sei_ap_translate,
0237     .alloc      = mvebu_sei_ap_alloc,
0238     .free       = irq_domain_free_irqs_parent,
0239 };
0240 
0241 static void mvebu_sei_cp_release_irq(struct mvebu_sei *sei, unsigned long hwirq)
0242 {
0243     mutex_lock(&sei->cp_msi_lock);
0244     clear_bit(hwirq, sei->cp_msi_bitmap);
0245     mutex_unlock(&sei->cp_msi_lock);
0246 }
0247 
0248 static int mvebu_sei_cp_domain_alloc(struct irq_domain *domain,
0249                      unsigned int virq, unsigned int nr_irqs,
0250                      void *args)
0251 {
0252     struct mvebu_sei *sei = domain->host_data;
0253     struct irq_fwspec fwspec;
0254     unsigned long hwirq;
0255     int ret;
0256 
0257     /* The software only supports single allocations for now */
0258     if (nr_irqs != 1)
0259         return -ENOTSUPP;
0260 
0261     mutex_lock(&sei->cp_msi_lock);
0262     hwirq = find_first_zero_bit(sei->cp_msi_bitmap,
0263                     sei->caps->cp_range.size);
0264     if (hwirq < sei->caps->cp_range.size)
0265         set_bit(hwirq, sei->cp_msi_bitmap);
0266     mutex_unlock(&sei->cp_msi_lock);
0267 
0268     if (hwirq == sei->caps->cp_range.size)
0269         return -ENOSPC;
0270 
0271     fwspec.fwnode = domain->parent->fwnode;
0272     fwspec.param_count = 1;
0273     fwspec.param[0] = hwirq + sei->caps->cp_range.first;
0274 
0275     ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
0276     if (ret)
0277         goto free_irq;
0278 
0279     irq_domain_set_info(domain, virq, hwirq,
0280                 &mvebu_sei_cp_irq_chip, sei,
0281                 handle_edge_irq, NULL, NULL);
0282 
0283     return 0;
0284 
0285 free_irq:
0286     mvebu_sei_cp_release_irq(sei, hwirq);
0287     return ret;
0288 }
0289 
0290 static void mvebu_sei_cp_domain_free(struct irq_domain *domain,
0291                      unsigned int virq, unsigned int nr_irqs)
0292 {
0293     struct mvebu_sei *sei = domain->host_data;
0294     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
0295 
0296     if (nr_irqs != 1 || d->hwirq >= sei->caps->cp_range.size) {
0297         dev_err(sei->dev, "Invalid hwirq %lu\n", d->hwirq);
0298         return;
0299     }
0300 
0301     mvebu_sei_cp_release_irq(sei, d->hwirq);
0302     irq_domain_free_irqs_parent(domain, virq, 1);
0303 }
0304 
0305 static const struct irq_domain_ops mvebu_sei_cp_domain_ops = {
0306     .alloc  = mvebu_sei_cp_domain_alloc,
0307     .free   = mvebu_sei_cp_domain_free,
0308 };
0309 
0310 static struct irq_chip mvebu_sei_msi_irq_chip = {
0311     .name       = "SEI pMSI",
0312     .irq_ack    = irq_chip_ack_parent,
0313     .irq_set_type   = irq_chip_set_type_parent,
0314 };
0315 
0316 static struct msi_domain_ops mvebu_sei_msi_ops = {
0317 };
0318 
0319 static struct msi_domain_info mvebu_sei_msi_domain_info = {
0320     .flags  = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS,
0321     .ops    = &mvebu_sei_msi_ops,
0322     .chip   = &mvebu_sei_msi_irq_chip,
0323 };
0324 
0325 static void mvebu_sei_handle_cascade_irq(struct irq_desc *desc)
0326 {
0327     struct mvebu_sei *sei = irq_desc_get_handler_data(desc);
0328     struct irq_chip *chip = irq_desc_get_chip(desc);
0329     u32 idx;
0330 
0331     chained_irq_enter(chip, desc);
0332 
0333     for (idx = 0; idx < SEI_IRQ_REG_COUNT; idx++) {
0334         unsigned long irqmap;
0335         int bit;
0336 
0337         irqmap = readl_relaxed(sei->base + GICP_SECR(idx));
0338         for_each_set_bit(bit, &irqmap, SEI_IRQ_COUNT_PER_REG) {
0339             unsigned long hwirq;
0340             int err;
0341 
0342             hwirq = idx * SEI_IRQ_COUNT_PER_REG + bit;
0343             err = generic_handle_domain_irq(sei->sei_domain, hwirq);
0344             if (unlikely(err))
0345                 dev_warn(sei->dev, "Spurious IRQ detected (hwirq %lu)\n", hwirq);
0346         }
0347     }
0348 
0349     chained_irq_exit(chip, desc);
0350 }
0351 
0352 static void mvebu_sei_reset(struct mvebu_sei *sei)
0353 {
0354     u32 reg_idx;
0355 
0356     /* Clear IRQ cause registers, mask all interrupts */
0357     for (reg_idx = 0; reg_idx < SEI_IRQ_REG_COUNT; reg_idx++) {
0358         writel_relaxed(0xFFFFFFFF, sei->base + GICP_SECR(reg_idx));
0359         writel_relaxed(0xFFFFFFFF, sei->base + GICP_SEMR(reg_idx));
0360     }
0361 }
0362 
0363 static int mvebu_sei_probe(struct platform_device *pdev)
0364 {
0365     struct device_node *node = pdev->dev.of_node;
0366     struct irq_domain *plat_domain;
0367     struct mvebu_sei *sei;
0368     u32 parent_irq;
0369     int ret;
0370 
0371     sei = devm_kzalloc(&pdev->dev, sizeof(*sei), GFP_KERNEL);
0372     if (!sei)
0373         return -ENOMEM;
0374 
0375     sei->dev = &pdev->dev;
0376 
0377     mutex_init(&sei->cp_msi_lock);
0378     raw_spin_lock_init(&sei->mask_lock);
0379 
0380     sei->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0381     sei->base = devm_ioremap_resource(sei->dev, sei->res);
0382     if (IS_ERR(sei->base))
0383         return PTR_ERR(sei->base);
0384 
0385     /* Retrieve the SEI capabilities with the interrupt ranges */
0386     sei->caps = of_device_get_match_data(&pdev->dev);
0387     if (!sei->caps) {
0388         dev_err(sei->dev,
0389             "Could not retrieve controller capabilities\n");
0390         return -EINVAL;
0391     }
0392 
0393     /*
0394      * Reserve the single (top-level) parent SPI IRQ from which all the
0395      * interrupts handled by this driver will be signaled.
0396      */
0397     parent_irq = irq_of_parse_and_map(node, 0);
0398     if (parent_irq <= 0) {
0399         dev_err(sei->dev, "Failed to retrieve top-level SPI IRQ\n");
0400         return -ENODEV;
0401     }
0402 
0403     /* Create the root SEI domain */
0404     sei->sei_domain = irq_domain_create_linear(of_node_to_fwnode(node),
0405                            (sei->caps->ap_range.size +
0406                             sei->caps->cp_range.size),
0407                            &mvebu_sei_domain_ops,
0408                            sei);
0409     if (!sei->sei_domain) {
0410         dev_err(sei->dev, "Failed to create SEI IRQ domain\n");
0411         ret = -ENOMEM;
0412         goto dispose_irq;
0413     }
0414 
0415     irq_domain_update_bus_token(sei->sei_domain, DOMAIN_BUS_NEXUS);
0416 
0417     /* Create the 'wired' domain */
0418     sei->ap_domain = irq_domain_create_hierarchy(sei->sei_domain, 0,
0419                              sei->caps->ap_range.size,
0420                              of_node_to_fwnode(node),
0421                              &mvebu_sei_ap_domain_ops,
0422                              sei);
0423     if (!sei->ap_domain) {
0424         dev_err(sei->dev, "Failed to create AP IRQ domain\n");
0425         ret = -ENOMEM;
0426         goto remove_sei_domain;
0427     }
0428 
0429     irq_domain_update_bus_token(sei->ap_domain, DOMAIN_BUS_WIRED);
0430 
0431     /* Create the 'MSI' domain */
0432     sei->cp_domain = irq_domain_create_hierarchy(sei->sei_domain, 0,
0433                              sei->caps->cp_range.size,
0434                              of_node_to_fwnode(node),
0435                              &mvebu_sei_cp_domain_ops,
0436                              sei);
0437     if (!sei->cp_domain) {
0438         pr_err("Failed to create CPs IRQ domain\n");
0439         ret = -ENOMEM;
0440         goto remove_ap_domain;
0441     }
0442 
0443     irq_domain_update_bus_token(sei->cp_domain, DOMAIN_BUS_GENERIC_MSI);
0444 
0445     plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
0446                              &mvebu_sei_msi_domain_info,
0447                              sei->cp_domain);
0448     if (!plat_domain) {
0449         pr_err("Failed to create CPs MSI domain\n");
0450         ret = -ENOMEM;
0451         goto remove_cp_domain;
0452     }
0453 
0454     mvebu_sei_reset(sei);
0455 
0456     irq_set_chained_handler_and_data(parent_irq,
0457                      mvebu_sei_handle_cascade_irq,
0458                      sei);
0459 
0460     return 0;
0461 
0462 remove_cp_domain:
0463     irq_domain_remove(sei->cp_domain);
0464 remove_ap_domain:
0465     irq_domain_remove(sei->ap_domain);
0466 remove_sei_domain:
0467     irq_domain_remove(sei->sei_domain);
0468 dispose_irq:
0469     irq_dispose_mapping(parent_irq);
0470 
0471     return ret;
0472 }
0473 
0474 static struct mvebu_sei_caps mvebu_sei_ap806_caps = {
0475     .ap_range = {
0476         .first = 0,
0477         .size = 21,
0478     },
0479     .cp_range = {
0480         .first = 21,
0481         .size = 43,
0482     },
0483 };
0484 
0485 static const struct of_device_id mvebu_sei_of_match[] = {
0486     {
0487         .compatible = "marvell,ap806-sei",
0488         .data = &mvebu_sei_ap806_caps,
0489     },
0490     {},
0491 };
0492 
0493 static struct platform_driver mvebu_sei_driver = {
0494     .probe  = mvebu_sei_probe,
0495     .driver = {
0496         .name = "mvebu-sei",
0497         .of_match_table = mvebu_sei_of_match,
0498     },
0499 };
0500 builtin_platform_driver(mvebu_sei_driver);