Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Microchip External Interrupt Controller driver
0004  *
0005  * Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries
0006  *
0007  * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
0008  */
0009 #include <linux/clk.h>
0010 #include <linux/delay.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/irqchip.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/syscore_ops.h>
0016 
0017 #include <dt-bindings/interrupt-controller/arm-gic.h>
0018 
0019 #define MCHP_EIC_GFCS           (0x0)
0020 #define MCHP_EIC_SCFG(x)        (0x4 + (x) * 0x4)
0021 #define MCHP_EIC_SCFG_EN        BIT(16)
0022 #define MCHP_EIC_SCFG_LVL       BIT(9)
0023 #define MCHP_EIC_SCFG_POL       BIT(8)
0024 
0025 #define MCHP_EIC_NIRQ           (2)
0026 
0027 /*
0028  * struct mchp_eic - EIC private data structure
0029  * @base: base address
0030  * @clk: peripheral clock
0031  * @domain: irq domain
0032  * @irqs: irqs b/w eic and gic
0033  * @scfg: backup for scfg registers (necessary for backup and self-refresh mode)
0034  * @wakeup_source: wakeup source mask
0035  */
0036 struct mchp_eic {
0037     void __iomem *base;
0038     struct clk *clk;
0039     struct irq_domain *domain;
0040     u32 irqs[MCHP_EIC_NIRQ];
0041     u32 scfg[MCHP_EIC_NIRQ];
0042     u32 wakeup_source;
0043 };
0044 
0045 static struct mchp_eic *eic;
0046 
0047 static void mchp_eic_irq_mask(struct irq_data *d)
0048 {
0049     unsigned int tmp;
0050 
0051     tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
0052     tmp &= ~MCHP_EIC_SCFG_EN;
0053     writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
0054 
0055     irq_chip_mask_parent(d);
0056 }
0057 
0058 static void mchp_eic_irq_unmask(struct irq_data *d)
0059 {
0060     unsigned int tmp;
0061 
0062     tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
0063     tmp |= MCHP_EIC_SCFG_EN;
0064     writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
0065 
0066     irq_chip_unmask_parent(d);
0067 }
0068 
0069 static int mchp_eic_irq_set_type(struct irq_data *d, unsigned int type)
0070 {
0071     unsigned int parent_irq_type;
0072     unsigned int tmp;
0073 
0074     tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
0075     tmp &= ~(MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL);
0076     switch (type) {
0077     case IRQ_TYPE_LEVEL_HIGH:
0078         tmp |= MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL;
0079         parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
0080         break;
0081     case IRQ_TYPE_LEVEL_LOW:
0082         tmp |= MCHP_EIC_SCFG_LVL;
0083         parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
0084         break;
0085     case IRQ_TYPE_EDGE_RISING:
0086         parent_irq_type = IRQ_TYPE_EDGE_RISING;
0087         break;
0088     case IRQ_TYPE_EDGE_FALLING:
0089         tmp |= MCHP_EIC_SCFG_POL;
0090         parent_irq_type = IRQ_TYPE_EDGE_RISING;
0091         break;
0092     default:
0093         return -EINVAL;
0094     }
0095 
0096     writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
0097 
0098     return irq_chip_set_type_parent(d, parent_irq_type);
0099 }
0100 
0101 static int mchp_eic_irq_set_wake(struct irq_data *d, unsigned int on)
0102 {
0103     irq_set_irq_wake(eic->irqs[d->hwirq], on);
0104     if (on)
0105         eic->wakeup_source |= BIT(d->hwirq);
0106     else
0107         eic->wakeup_source &= ~BIT(d->hwirq);
0108 
0109     return 0;
0110 }
0111 
0112 static int mchp_eic_irq_suspend(void)
0113 {
0114     unsigned int hwirq;
0115 
0116     for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
0117         eic->scfg[hwirq] = readl_relaxed(eic->base +
0118                          MCHP_EIC_SCFG(hwirq));
0119 
0120     if (!eic->wakeup_source)
0121         clk_disable_unprepare(eic->clk);
0122 
0123     return 0;
0124 }
0125 
0126 static void mchp_eic_irq_resume(void)
0127 {
0128     unsigned int hwirq;
0129 
0130     if (!eic->wakeup_source)
0131         clk_prepare_enable(eic->clk);
0132 
0133     for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
0134         writel_relaxed(eic->scfg[hwirq], eic->base +
0135                    MCHP_EIC_SCFG(hwirq));
0136 }
0137 
0138 static struct syscore_ops mchp_eic_syscore_ops = {
0139     .suspend = mchp_eic_irq_suspend,
0140     .resume = mchp_eic_irq_resume,
0141 };
0142 
0143 static struct irq_chip mchp_eic_chip = {
0144     .name       = "eic",
0145     .flags      = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED,
0146     .irq_mask   = mchp_eic_irq_mask,
0147     .irq_unmask = mchp_eic_irq_unmask,
0148     .irq_set_type   = mchp_eic_irq_set_type,
0149     .irq_ack    = irq_chip_ack_parent,
0150     .irq_eoi    = irq_chip_eoi_parent,
0151     .irq_retrigger  = irq_chip_retrigger_hierarchy,
0152     .irq_set_wake   = mchp_eic_irq_set_wake,
0153 };
0154 
0155 static int mchp_eic_domain_alloc(struct irq_domain *domain, unsigned int virq,
0156                  unsigned int nr_irqs, void *data)
0157 {
0158     struct irq_fwspec *fwspec = data;
0159     struct irq_fwspec parent_fwspec;
0160     irq_hw_number_t hwirq;
0161     unsigned int type;
0162     int ret;
0163 
0164     if (WARN_ON(nr_irqs != 1))
0165         return -EINVAL;
0166 
0167     ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
0168     if (ret || hwirq >= MCHP_EIC_NIRQ)
0169         return ret;
0170 
0171     switch (type) {
0172     case IRQ_TYPE_EDGE_RISING:
0173     case IRQ_TYPE_LEVEL_HIGH:
0174         break;
0175     case IRQ_TYPE_EDGE_FALLING:
0176         type = IRQ_TYPE_EDGE_RISING;
0177         break;
0178     case IRQ_TYPE_LEVEL_LOW:
0179         type = IRQ_TYPE_LEVEL_HIGH;
0180         break;
0181     default:
0182         return -EINVAL;
0183     }
0184 
0185     irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &mchp_eic_chip, eic);
0186 
0187     parent_fwspec.fwnode = domain->parent->fwnode;
0188     parent_fwspec.param_count = 3;
0189     parent_fwspec.param[0] = GIC_SPI;
0190     parent_fwspec.param[1] = eic->irqs[hwirq];
0191     parent_fwspec.param[2] = type;
0192 
0193     return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
0194 }
0195 
0196 static const struct irq_domain_ops mchp_eic_domain_ops = {
0197     .translate  = irq_domain_translate_twocell,
0198     .alloc      = mchp_eic_domain_alloc,
0199     .free       = irq_domain_free_irqs_common,
0200 };
0201 
0202 static int mchp_eic_init(struct device_node *node, struct device_node *parent)
0203 {
0204     struct irq_domain *parent_domain = NULL;
0205     int ret, i;
0206 
0207     eic = kzalloc(sizeof(*eic), GFP_KERNEL);
0208     if (!eic)
0209         return -ENOMEM;
0210 
0211     eic->base = of_iomap(node, 0);
0212     if (!eic->base) {
0213         ret = -ENOMEM;
0214         goto free;
0215     }
0216 
0217     parent_domain = irq_find_host(parent);
0218     if (!parent_domain) {
0219         ret = -ENODEV;
0220         goto unmap;
0221     }
0222 
0223     eic->clk = of_clk_get_by_name(node, "pclk");
0224     if (IS_ERR(eic->clk)) {
0225         ret = PTR_ERR(eic->clk);
0226         goto unmap;
0227     }
0228 
0229     ret = clk_prepare_enable(eic->clk);
0230     if (ret)
0231         goto unmap;
0232 
0233     for (i = 0; i < MCHP_EIC_NIRQ; i++) {
0234         struct of_phandle_args irq;
0235 
0236         /* Disable it, if any. */
0237         writel_relaxed(0UL, eic->base + MCHP_EIC_SCFG(i));
0238 
0239         ret = of_irq_parse_one(node, i, &irq);
0240         if (ret)
0241             goto clk_unprepare;
0242 
0243         if (WARN_ON(irq.args_count != 3)) {
0244             ret = -EINVAL;
0245             goto clk_unprepare;
0246         }
0247 
0248         eic->irqs[i] = irq.args[1];
0249     }
0250 
0251     eic->domain = irq_domain_add_hierarchy(parent_domain, 0, MCHP_EIC_NIRQ,
0252                            node, &mchp_eic_domain_ops, eic);
0253     if (!eic->domain) {
0254         pr_err("%pOF: Failed to add domain\n", node);
0255         ret = -ENODEV;
0256         goto clk_unprepare;
0257     }
0258 
0259     register_syscore_ops(&mchp_eic_syscore_ops);
0260 
0261     pr_info("%pOF: EIC registered, nr_irqs %u\n", node, MCHP_EIC_NIRQ);
0262 
0263     return 0;
0264 
0265 clk_unprepare:
0266     clk_disable_unprepare(eic->clk);
0267 unmap:
0268     iounmap(eic->base);
0269 free:
0270     kfree(eic);
0271     return ret;
0272 }
0273 
0274 IRQCHIP_PLATFORM_DRIVER_BEGIN(mchp_eic)
0275 IRQCHIP_MATCH("microchip,sama7g5-eic", mchp_eic_init)
0276 IRQCHIP_PLATFORM_DRIVER_END(mchp_eic)
0277 
0278 MODULE_DESCRIPTION("Microchip External Interrupt Controller");
0279 MODULE_LICENSE("GPL v2");
0280 MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");