Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (C) 2017 Marvell
0003  *
0004  * Hanna Hawa <hannah@marvell.com>
0005  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
0006  *
0007  * This file is licensed under the terms of the GNU General Public
0008  * License version 2. This program is licensed "as is" without any
0009  * warranty of any kind, whether express or implied.
0010  */
0011 
0012 #include <linux/interrupt.h>
0013 #include <linux/irq.h>
0014 #include <linux/irqchip.h>
0015 #include <linux/irqdomain.h>
0016 #include <linux/jump_label.h>
0017 #include <linux/kernel.h>
0018 #include <linux/msi.h>
0019 #include <linux/of_irq.h>
0020 #include <linux/of_platform.h>
0021 #include <linux/platform_device.h>
0022 
0023 #include <dt-bindings/interrupt-controller/mvebu-icu.h>
0024 
0025 /* ICU registers */
0026 #define ICU_SETSPI_NSR_AL   0x10
0027 #define ICU_SETSPI_NSR_AH   0x14
0028 #define ICU_CLRSPI_NSR_AL   0x18
0029 #define ICU_CLRSPI_NSR_AH   0x1c
0030 #define ICU_SET_SEI_AL      0x50
0031 #define ICU_SET_SEI_AH      0x54
0032 #define ICU_CLR_SEI_AL      0x58
0033 #define ICU_CLR_SEI_AH      0x5C
0034 #define ICU_INT_CFG(x)          (0x100 + 4 * (x))
0035 #define   ICU_INT_ENABLE    BIT(24)
0036 #define   ICU_IS_EDGE       BIT(28)
0037 #define   ICU_GROUP_SHIFT   29
0038 
0039 /* ICU definitions */
0040 #define ICU_MAX_IRQS        207
0041 #define ICU_SATA0_ICU_ID    109
0042 #define ICU_SATA1_ICU_ID    107
0043 
0044 struct mvebu_icu_subset_data {
0045     unsigned int icu_group;
0046     unsigned int offset_set_ah;
0047     unsigned int offset_set_al;
0048     unsigned int offset_clr_ah;
0049     unsigned int offset_clr_al;
0050 };
0051 
0052 struct mvebu_icu {
0053     void __iomem *base;
0054     struct device *dev;
0055 };
0056 
0057 struct mvebu_icu_msi_data {
0058     struct mvebu_icu *icu;
0059     atomic_t initialized;
0060     const struct mvebu_icu_subset_data *subset_data;
0061 };
0062 
0063 struct mvebu_icu_irq_data {
0064     struct mvebu_icu *icu;
0065     unsigned int icu_group;
0066     unsigned int type;
0067 };
0068 
0069 static DEFINE_STATIC_KEY_FALSE(legacy_bindings);
0070 
0071 static void mvebu_icu_init(struct mvebu_icu *icu,
0072                struct mvebu_icu_msi_data *msi_data,
0073                struct msi_msg *msg)
0074 {
0075     const struct mvebu_icu_subset_data *subset = msi_data->subset_data;
0076 
0077     if (atomic_cmpxchg(&msi_data->initialized, false, true))
0078         return;
0079 
0080     /* Set 'SET' ICU SPI message address in AP */
0081     writel_relaxed(msg[0].address_hi, icu->base + subset->offset_set_ah);
0082     writel_relaxed(msg[0].address_lo, icu->base + subset->offset_set_al);
0083 
0084     if (subset->icu_group != ICU_GRP_NSR)
0085         return;
0086 
0087     /* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */
0088     writel_relaxed(msg[1].address_hi, icu->base + subset->offset_clr_ah);
0089     writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al);
0090 }
0091 
0092 static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
0093 {
0094     struct irq_data *d = irq_get_irq_data(desc->irq);
0095     struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d->domain);
0096     struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
0097     struct mvebu_icu *icu = icu_irqd->icu;
0098     unsigned int icu_int;
0099 
0100     if (msg->address_lo || msg->address_hi) {
0101         /* One off initialization per domain */
0102         mvebu_icu_init(icu, msi_data, msg);
0103         /* Configure the ICU with irq number & type */
0104         icu_int = msg->data | ICU_INT_ENABLE;
0105         if (icu_irqd->type & IRQ_TYPE_EDGE_RISING)
0106             icu_int |= ICU_IS_EDGE;
0107         icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT;
0108     } else {
0109         /* De-configure the ICU */
0110         icu_int = 0;
0111     }
0112 
0113     writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq));
0114 
0115     /*
0116      * The SATA unit has 2 ports, and a dedicated ICU entry per
0117      * port. The ahci sata driver supports only one irq interrupt
0118      * per SATA unit. To solve this conflict, we configure the 2
0119      * SATA wired interrupts in the south bridge into 1 GIC
0120      * interrupt in the north bridge. Even if only a single port
0121      * is enabled, if sata node is enabled, both interrupts are
0122      * configured (regardless of which port is actually in use).
0123      */
0124     if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) {
0125         writel_relaxed(icu_int,
0126                    icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID));
0127         writel_relaxed(icu_int,
0128                    icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID));
0129     }
0130 }
0131 
0132 static struct irq_chip mvebu_icu_nsr_chip = {
0133     .name           = "ICU-NSR",
0134     .irq_mask       = irq_chip_mask_parent,
0135     .irq_unmask     = irq_chip_unmask_parent,
0136     .irq_eoi        = irq_chip_eoi_parent,
0137     .irq_set_type       = irq_chip_set_type_parent,
0138     .irq_set_affinity   = irq_chip_set_affinity_parent,
0139 };
0140 
0141 static struct irq_chip mvebu_icu_sei_chip = {
0142     .name           = "ICU-SEI",
0143     .irq_ack        = irq_chip_ack_parent,
0144     .irq_mask       = irq_chip_mask_parent,
0145     .irq_unmask     = irq_chip_unmask_parent,
0146     .irq_set_type       = irq_chip_set_type_parent,
0147     .irq_set_affinity   = irq_chip_set_affinity_parent,
0148 };
0149 
0150 static int
0151 mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
0152                    unsigned long *hwirq, unsigned int *type)
0153 {
0154     struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d);
0155     struct mvebu_icu *icu = platform_msi_get_host_data(d);
0156     unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2;
0157 
0158     /* Check the count of the parameters in dt */
0159     if (WARN_ON(fwspec->param_count != param_count)) {
0160         dev_err(icu->dev, "wrong ICU parameter count %d\n",
0161             fwspec->param_count);
0162         return -EINVAL;
0163     }
0164 
0165     if (static_branch_unlikely(&legacy_bindings)) {
0166         *hwirq = fwspec->param[1];
0167         *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
0168         if (fwspec->param[0] != ICU_GRP_NSR) {
0169             dev_err(icu->dev, "wrong ICU group type %x\n",
0170                 fwspec->param[0]);
0171             return -EINVAL;
0172         }
0173     } else {
0174         *hwirq = fwspec->param[0];
0175         *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
0176 
0177         /*
0178          * The ICU receives level interrupts. While the NSR are also
0179          * level interrupts, SEI are edge interrupts. Force the type
0180          * here in this case. Please note that this makes the interrupt
0181          * handling unreliable.
0182          */
0183         if (msi_data->subset_data->icu_group == ICU_GRP_SEI)
0184             *type = IRQ_TYPE_EDGE_RISING;
0185     }
0186 
0187     if (*hwirq >= ICU_MAX_IRQS) {
0188         dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq);
0189         return -EINVAL;
0190     }
0191 
0192     return 0;
0193 }
0194 
0195 static int
0196 mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
0197                unsigned int nr_irqs, void *args)
0198 {
0199     int err;
0200     unsigned long hwirq;
0201     struct irq_fwspec *fwspec = args;
0202     struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(domain);
0203     struct mvebu_icu *icu = msi_data->icu;
0204     struct mvebu_icu_irq_data *icu_irqd;
0205     struct irq_chip *chip = &mvebu_icu_nsr_chip;
0206 
0207     icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL);
0208     if (!icu_irqd)
0209         return -ENOMEM;
0210 
0211     err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq,
0212                          &icu_irqd->type);
0213     if (err) {
0214         dev_err(icu->dev, "failed to translate ICU parameters\n");
0215         goto free_irqd;
0216     }
0217 
0218     if (static_branch_unlikely(&legacy_bindings))
0219         icu_irqd->icu_group = fwspec->param[0];
0220     else
0221         icu_irqd->icu_group = msi_data->subset_data->icu_group;
0222     icu_irqd->icu = icu;
0223 
0224     err = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
0225     if (err) {
0226         dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n");
0227         goto free_irqd;
0228     }
0229 
0230     /* Make sure there is no interrupt left pending by the firmware */
0231     err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false);
0232     if (err)
0233         goto free_msi;
0234 
0235     if (icu_irqd->icu_group == ICU_GRP_SEI)
0236         chip = &mvebu_icu_sei_chip;
0237 
0238     err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
0239                         chip, icu_irqd);
0240     if (err) {
0241         dev_err(icu->dev, "failed to set the data to IRQ domain\n");
0242         goto free_msi;
0243     }
0244 
0245     return 0;
0246 
0247 free_msi:
0248     platform_msi_device_domain_free(domain, virq, nr_irqs);
0249 free_irqd:
0250     kfree(icu_irqd);
0251     return err;
0252 }
0253 
0254 static void
0255 mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq,
0256               unsigned int nr_irqs)
0257 {
0258     struct irq_data *d = irq_get_irq_data(virq);
0259     struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
0260 
0261     kfree(icu_irqd);
0262 
0263     platform_msi_device_domain_free(domain, virq, nr_irqs);
0264 }
0265 
0266 static const struct irq_domain_ops mvebu_icu_domain_ops = {
0267     .translate = mvebu_icu_irq_domain_translate,
0268     .alloc     = mvebu_icu_irq_domain_alloc,
0269     .free      = mvebu_icu_irq_domain_free,
0270 };
0271 
0272 static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data = {
0273     .icu_group = ICU_GRP_NSR,
0274     .offset_set_ah = ICU_SETSPI_NSR_AH,
0275     .offset_set_al = ICU_SETSPI_NSR_AL,
0276     .offset_clr_ah = ICU_CLRSPI_NSR_AH,
0277     .offset_clr_al = ICU_CLRSPI_NSR_AL,
0278 };
0279 
0280 static const struct mvebu_icu_subset_data mvebu_icu_sei_subset_data = {
0281     .icu_group = ICU_GRP_SEI,
0282     .offset_set_ah = ICU_SET_SEI_AH,
0283     .offset_set_al = ICU_SET_SEI_AL,
0284 };
0285 
0286 static const struct of_device_id mvebu_icu_subset_of_match[] = {
0287     {
0288         .compatible = "marvell,cp110-icu-nsr",
0289         .data = &mvebu_icu_nsr_subset_data,
0290     },
0291     {
0292         .compatible = "marvell,cp110-icu-sei",
0293         .data = &mvebu_icu_sei_subset_data,
0294     },
0295     {},
0296 };
0297 
0298 static int mvebu_icu_subset_probe(struct platform_device *pdev)
0299 {
0300     struct mvebu_icu_msi_data *msi_data;
0301     struct device_node *msi_parent_dn;
0302     struct device *dev = &pdev->dev;
0303     struct irq_domain *irq_domain;
0304 
0305     msi_data = devm_kzalloc(dev, sizeof(*msi_data), GFP_KERNEL);
0306     if (!msi_data)
0307         return -ENOMEM;
0308 
0309     if (static_branch_unlikely(&legacy_bindings)) {
0310         msi_data->icu = dev_get_drvdata(dev);
0311         msi_data->subset_data = &mvebu_icu_nsr_subset_data;
0312     } else {
0313         msi_data->icu = dev_get_drvdata(dev->parent);
0314         msi_data->subset_data = of_device_get_match_data(dev);
0315     }
0316 
0317     dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
0318                         DOMAIN_BUS_PLATFORM_MSI);
0319     if (!dev->msi.domain)
0320         return -EPROBE_DEFER;
0321 
0322     msi_parent_dn = irq_domain_get_of_node(dev->msi.domain);
0323     if (!msi_parent_dn)
0324         return -ENODEV;
0325 
0326     irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS,
0327                                 mvebu_icu_write_msg,
0328                                 &mvebu_icu_domain_ops,
0329                                 msi_data);
0330     if (!irq_domain) {
0331         dev_err(dev, "Failed to create ICU MSI domain\n");
0332         return -ENOMEM;
0333     }
0334 
0335     return 0;
0336 }
0337 
0338 static struct platform_driver mvebu_icu_subset_driver = {
0339     .probe  = mvebu_icu_subset_probe,
0340     .driver = {
0341         .name = "mvebu-icu-subset",
0342         .of_match_table = mvebu_icu_subset_of_match,
0343     },
0344 };
0345 builtin_platform_driver(mvebu_icu_subset_driver);
0346 
0347 static int mvebu_icu_probe(struct platform_device *pdev)
0348 {
0349     struct mvebu_icu *icu;
0350     int i;
0351 
0352     icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu),
0353                GFP_KERNEL);
0354     if (!icu)
0355         return -ENOMEM;
0356 
0357     icu->dev = &pdev->dev;
0358 
0359     icu->base = devm_platform_ioremap_resource(pdev, 0);
0360     if (IS_ERR(icu->base))
0361         return PTR_ERR(icu->base);
0362 
0363     /*
0364      * Legacy bindings: ICU is one node with one MSI parent: force manually
0365      *                  the probe of the NSR interrupts side.
0366      * New bindings: ICU node has children, one per interrupt controller
0367      *               having its own MSI parent: call platform_populate().
0368      * All ICU instances should use the same bindings.
0369      */
0370     if (!of_get_child_count(pdev->dev.of_node))
0371         static_branch_enable(&legacy_bindings);
0372 
0373     /*
0374      * Clean all ICU interrupts of type NSR and SEI, required to
0375      * avoid unpredictable SPI assignments done by firmware.
0376      */
0377     for (i = 0 ; i < ICU_MAX_IRQS ; i++) {
0378         u32 icu_int, icu_grp;
0379 
0380         icu_int = readl_relaxed(icu->base + ICU_INT_CFG(i));
0381         icu_grp = icu_int >> ICU_GROUP_SHIFT;
0382 
0383         if (icu_grp == ICU_GRP_NSR ||
0384             (icu_grp == ICU_GRP_SEI &&
0385              !static_branch_unlikely(&legacy_bindings)))
0386             writel_relaxed(0x0, icu->base + ICU_INT_CFG(i));
0387     }
0388 
0389     platform_set_drvdata(pdev, icu);
0390 
0391     if (static_branch_unlikely(&legacy_bindings))
0392         return mvebu_icu_subset_probe(pdev);
0393     else
0394         return devm_of_platform_populate(&pdev->dev);
0395 }
0396 
0397 static const struct of_device_id mvebu_icu_of_match[] = {
0398     { .compatible = "marvell,cp110-icu", },
0399     {},
0400 };
0401 
0402 static struct platform_driver mvebu_icu_driver = {
0403     .probe  = mvebu_icu_probe,
0404     .driver = {
0405         .name = "mvebu-icu",
0406         .of_match_table = mvebu_icu_of_match,
0407     },
0408 };
0409 builtin_platform_driver(mvebu_icu_driver);