Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2016 MediaTek Inc.
0004  * Author: Youlin.Pei <youlin.pei@mediatek.com>
0005  */
0006 
0007 #include <linux/interrupt.h>
0008 #include <linux/io.h>
0009 #include <linux/irq.h>
0010 #include <linux/irqchip.h>
0011 #include <linux/irqdomain.h>
0012 #include <linux/of.h>
0013 #include <linux/of_irq.h>
0014 #include <linux/of_address.h>
0015 #include <linux/slab.h>
0016 #include <linux/syscore_ops.h>
0017 
0018 #define CIRQ_ACK    0x40
0019 #define CIRQ_MASK_SET   0xc0
0020 #define CIRQ_MASK_CLR   0x100
0021 #define CIRQ_SENS_SET   0x180
0022 #define CIRQ_SENS_CLR   0x1c0
0023 #define CIRQ_POL_SET    0x240
0024 #define CIRQ_POL_CLR    0x280
0025 #define CIRQ_CONTROL    0x300
0026 
0027 #define CIRQ_EN 0x1
0028 #define CIRQ_EDGE   0x2
0029 #define CIRQ_FLUSH  0x4
0030 
0031 struct mtk_cirq_chip_data {
0032     void __iomem *base;
0033     unsigned int ext_irq_start;
0034     unsigned int ext_irq_end;
0035     struct irq_domain *domain;
0036 };
0037 
0038 static struct mtk_cirq_chip_data *cirq_data;
0039 
0040 static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
0041 {
0042     struct mtk_cirq_chip_data *chip_data = data->chip_data;
0043     unsigned int cirq_num = data->hwirq;
0044     u32 mask = 1 << (cirq_num % 32);
0045 
0046     writel_relaxed(mask, chip_data->base + offset + (cirq_num / 32) * 4);
0047 }
0048 
0049 static void mtk_cirq_mask(struct irq_data *data)
0050 {
0051     mtk_cirq_write_mask(data, CIRQ_MASK_SET);
0052     irq_chip_mask_parent(data);
0053 }
0054 
0055 static void mtk_cirq_unmask(struct irq_data *data)
0056 {
0057     mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
0058     irq_chip_unmask_parent(data);
0059 }
0060 
0061 static int mtk_cirq_set_type(struct irq_data *data, unsigned int type)
0062 {
0063     int ret;
0064 
0065     switch (type & IRQ_TYPE_SENSE_MASK) {
0066     case IRQ_TYPE_EDGE_FALLING:
0067         mtk_cirq_write_mask(data, CIRQ_POL_CLR);
0068         mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
0069         break;
0070     case IRQ_TYPE_EDGE_RISING:
0071         mtk_cirq_write_mask(data, CIRQ_POL_SET);
0072         mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
0073         break;
0074     case IRQ_TYPE_LEVEL_LOW:
0075         mtk_cirq_write_mask(data, CIRQ_POL_CLR);
0076         mtk_cirq_write_mask(data, CIRQ_SENS_SET);
0077         break;
0078     case IRQ_TYPE_LEVEL_HIGH:
0079         mtk_cirq_write_mask(data, CIRQ_POL_SET);
0080         mtk_cirq_write_mask(data, CIRQ_SENS_SET);
0081         break;
0082     default:
0083         break;
0084     }
0085 
0086     data = data->parent_data;
0087     ret = data->chip->irq_set_type(data, type);
0088     return ret;
0089 }
0090 
0091 static struct irq_chip mtk_cirq_chip = {
0092     .name           = "MT_CIRQ",
0093     .irq_mask       = mtk_cirq_mask,
0094     .irq_unmask     = mtk_cirq_unmask,
0095     .irq_eoi        = irq_chip_eoi_parent,
0096     .irq_set_type       = mtk_cirq_set_type,
0097     .irq_retrigger      = irq_chip_retrigger_hierarchy,
0098 #ifdef CONFIG_SMP
0099     .irq_set_affinity   = irq_chip_set_affinity_parent,
0100 #endif
0101 };
0102 
0103 static int mtk_cirq_domain_translate(struct irq_domain *d,
0104                      struct irq_fwspec *fwspec,
0105                      unsigned long *hwirq,
0106                      unsigned int *type)
0107 {
0108     if (is_of_node(fwspec->fwnode)) {
0109         if (fwspec->param_count != 3)
0110             return -EINVAL;
0111 
0112         /* No PPI should point to this domain */
0113         if (fwspec->param[0] != 0)
0114             return -EINVAL;
0115 
0116         /* cirq support irq number check */
0117         if (fwspec->param[1] < cirq_data->ext_irq_start ||
0118             fwspec->param[1] > cirq_data->ext_irq_end)
0119             return -EINVAL;
0120 
0121         *hwirq = fwspec->param[1] - cirq_data->ext_irq_start;
0122         *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
0123         return 0;
0124     }
0125 
0126     return -EINVAL;
0127 }
0128 
0129 static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
0130                  unsigned int nr_irqs, void *arg)
0131 {
0132     int ret;
0133     irq_hw_number_t hwirq;
0134     unsigned int type;
0135     struct irq_fwspec *fwspec = arg;
0136     struct irq_fwspec parent_fwspec = *fwspec;
0137 
0138     ret = mtk_cirq_domain_translate(domain, fwspec, &hwirq, &type);
0139     if (ret)
0140         return ret;
0141 
0142     if (WARN_ON(nr_irqs != 1))
0143         return -EINVAL;
0144 
0145     irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
0146                       &mtk_cirq_chip,
0147                       domain->host_data);
0148 
0149     parent_fwspec.fwnode = domain->parent->fwnode;
0150     return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
0151                         &parent_fwspec);
0152 }
0153 
0154 static const struct irq_domain_ops cirq_domain_ops = {
0155     .translate  = mtk_cirq_domain_translate,
0156     .alloc      = mtk_cirq_domain_alloc,
0157     .free       = irq_domain_free_irqs_common,
0158 };
0159 
0160 #ifdef CONFIG_PM_SLEEP
0161 static int mtk_cirq_suspend(void)
0162 {
0163     u32 value, mask;
0164     unsigned int irq, hwirq_num;
0165     bool pending, masked;
0166     int i, pendret, maskret;
0167 
0168     /*
0169      * When external interrupts happened, CIRQ will record the status
0170      * even CIRQ is not enabled. When execute flush command, CIRQ will
0171      * resend the signals according to the status. So if don't clear the
0172      * status, CIRQ will resend the wrong signals.
0173      *
0174      * arch_suspend_disable_irqs() will be called before CIRQ suspend
0175      * callback. If clear all the status simply, the external interrupts
0176      * which happened between arch_suspend_disable_irqs and CIRQ suspend
0177      * callback will be lost. Using following steps to avoid this issue;
0178      *
0179      * - Iterate over all the CIRQ supported interrupts;
0180      * - For each interrupt, inspect its pending and masked status at GIC
0181      *   level;
0182      * - If pending and unmasked, it happened between
0183      *   arch_suspend_disable_irqs and CIRQ suspend callback, don't ACK
0184      *   it. Otherwise, ACK it.
0185      */
0186     hwirq_num = cirq_data->ext_irq_end - cirq_data->ext_irq_start + 1;
0187     for (i = 0; i < hwirq_num; i++) {
0188         irq = irq_find_mapping(cirq_data->domain, i);
0189         if (irq) {
0190             pendret = irq_get_irqchip_state(irq,
0191                             IRQCHIP_STATE_PENDING,
0192                             &pending);
0193 
0194             maskret = irq_get_irqchip_state(irq,
0195                             IRQCHIP_STATE_MASKED,
0196                             &masked);
0197 
0198             if (pendret == 0 && maskret == 0 &&
0199                 (pending && !masked))
0200                 continue;
0201         }
0202 
0203         mask = 1 << (i % 32);
0204         writel_relaxed(mask, cirq_data->base + CIRQ_ACK + (i / 32) * 4);
0205     }
0206 
0207     /* set edge_only mode, record edge-triggerd interrupts */
0208     /* enable cirq */
0209     value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
0210     value |= (CIRQ_EDGE | CIRQ_EN);
0211     writel_relaxed(value, cirq_data->base + CIRQ_CONTROL);
0212 
0213     return 0;
0214 }
0215 
0216 static void mtk_cirq_resume(void)
0217 {
0218     u32 value;
0219 
0220     /* flush recorded interrupts, will send signals to parent controller */
0221     value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
0222     writel_relaxed(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL);
0223 
0224     /* disable cirq */
0225     value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
0226     value &= ~(CIRQ_EDGE | CIRQ_EN);
0227     writel_relaxed(value, cirq_data->base + CIRQ_CONTROL);
0228 }
0229 
0230 static struct syscore_ops mtk_cirq_syscore_ops = {
0231     .suspend    = mtk_cirq_suspend,
0232     .resume     = mtk_cirq_resume,
0233 };
0234 
0235 static void mtk_cirq_syscore_init(void)
0236 {
0237     register_syscore_ops(&mtk_cirq_syscore_ops);
0238 }
0239 #else
0240 static inline void mtk_cirq_syscore_init(void) {}
0241 #endif
0242 
0243 static int __init mtk_cirq_of_init(struct device_node *node,
0244                    struct device_node *parent)
0245 {
0246     struct irq_domain *domain, *domain_parent;
0247     unsigned int irq_num;
0248     int ret;
0249 
0250     domain_parent = irq_find_host(parent);
0251     if (!domain_parent) {
0252         pr_err("mtk_cirq: interrupt-parent not found\n");
0253         return -EINVAL;
0254     }
0255 
0256     cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
0257     if (!cirq_data)
0258         return -ENOMEM;
0259 
0260     cirq_data->base = of_iomap(node, 0);
0261     if (!cirq_data->base) {
0262         pr_err("mtk_cirq: unable to map cirq register\n");
0263         ret = -ENXIO;
0264         goto out_free;
0265     }
0266 
0267     ret = of_property_read_u32_index(node, "mediatek,ext-irq-range", 0,
0268                      &cirq_data->ext_irq_start);
0269     if (ret)
0270         goto out_unmap;
0271 
0272     ret = of_property_read_u32_index(node, "mediatek,ext-irq-range", 1,
0273                      &cirq_data->ext_irq_end);
0274     if (ret)
0275         goto out_unmap;
0276 
0277     irq_num = cirq_data->ext_irq_end - cirq_data->ext_irq_start + 1;
0278     domain = irq_domain_add_hierarchy(domain_parent, 0,
0279                       irq_num, node,
0280                       &cirq_domain_ops, cirq_data);
0281     if (!domain) {
0282         ret = -ENOMEM;
0283         goto out_unmap;
0284     }
0285     cirq_data->domain = domain;
0286 
0287     mtk_cirq_syscore_init();
0288 
0289     return 0;
0290 
0291 out_unmap:
0292     iounmap(cirq_data->base);
0293 out_free:
0294     kfree(cirq_data);
0295     return ret;
0296 }
0297 
0298 IRQCHIP_DECLARE(mtk_cirq, "mediatek,mtk-cirq", mtk_cirq_of_init);