Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2014 MediaTek Inc.
0004  * Author: Joe.C <yingjoe.chen@mediatek.com>
0005  */
0006 
0007 #include <linux/irq.h>
0008 #include <linux/irqchip.h>
0009 #include <linux/irqdomain.h>
0010 #include <linux/of.h>
0011 #include <linux/of_irq.h>
0012 #include <linux/of_address.h>
0013 #include <linux/io.h>
0014 #include <linux/slab.h>
0015 #include <linux/spinlock.h>
0016 
0017 struct mtk_sysirq_chip_data {
0018     raw_spinlock_t lock;
0019     u32 nr_intpol_bases;
0020     void __iomem **intpol_bases;
0021     u32 *intpol_words;
0022     u8 *intpol_idx;
0023     u16 *which_word;
0024 };
0025 
0026 static int mtk_sysirq_set_type(struct irq_data *data, unsigned int type)
0027 {
0028     irq_hw_number_t hwirq = data->hwirq;
0029     struct mtk_sysirq_chip_data *chip_data = data->chip_data;
0030     u8 intpol_idx = chip_data->intpol_idx[hwirq];
0031     void __iomem *base;
0032     u32 offset, reg_index, value;
0033     unsigned long flags;
0034     int ret;
0035 
0036     base = chip_data->intpol_bases[intpol_idx];
0037     reg_index = chip_data->which_word[hwirq];
0038     offset = hwirq & 0x1f;
0039 
0040     raw_spin_lock_irqsave(&chip_data->lock, flags);
0041     value = readl_relaxed(base + reg_index * 4);
0042     if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) {
0043         if (type == IRQ_TYPE_LEVEL_LOW)
0044             type = IRQ_TYPE_LEVEL_HIGH;
0045         else
0046             type = IRQ_TYPE_EDGE_RISING;
0047         value |= (1 << offset);
0048     } else {
0049         value &= ~(1 << offset);
0050     }
0051 
0052     writel_relaxed(value, base + reg_index * 4);
0053 
0054     data = data->parent_data;
0055     ret = data->chip->irq_set_type(data, type);
0056     raw_spin_unlock_irqrestore(&chip_data->lock, flags);
0057     return ret;
0058 }
0059 
0060 static struct irq_chip mtk_sysirq_chip = {
0061     .name           = "MT_SYSIRQ",
0062     .irq_mask       = irq_chip_mask_parent,
0063     .irq_unmask     = irq_chip_unmask_parent,
0064     .irq_eoi        = irq_chip_eoi_parent,
0065     .irq_set_type       = mtk_sysirq_set_type,
0066     .irq_retrigger      = irq_chip_retrigger_hierarchy,
0067     .irq_set_affinity   = irq_chip_set_affinity_parent,
0068     .flags          = IRQCHIP_SKIP_SET_WAKE,
0069 };
0070 
0071 static int mtk_sysirq_domain_translate(struct irq_domain *d,
0072                        struct irq_fwspec *fwspec,
0073                        unsigned long *hwirq,
0074                        unsigned int *type)
0075 {
0076     if (is_of_node(fwspec->fwnode)) {
0077         if (fwspec->param_count != 3)
0078             return -EINVAL;
0079 
0080         /* No PPI should point to this domain */
0081         if (fwspec->param[0] != 0)
0082             return -EINVAL;
0083 
0084         *hwirq = fwspec->param[1];
0085         *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
0086         return 0;
0087     }
0088 
0089     return -EINVAL;
0090 }
0091 
0092 static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
0093                    unsigned int nr_irqs, void *arg)
0094 {
0095     int i;
0096     irq_hw_number_t hwirq;
0097     struct irq_fwspec *fwspec = arg;
0098     struct irq_fwspec gic_fwspec = *fwspec;
0099 
0100     if (fwspec->param_count != 3)
0101         return -EINVAL;
0102 
0103     /* sysirq doesn't support PPI */
0104     if (fwspec->param[0])
0105         return -EINVAL;
0106 
0107     hwirq = fwspec->param[1];
0108     for (i = 0; i < nr_irqs; i++)
0109         irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
0110                           &mtk_sysirq_chip,
0111                           domain->host_data);
0112 
0113     gic_fwspec.fwnode = domain->parent->fwnode;
0114     return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec);
0115 }
0116 
0117 static const struct irq_domain_ops sysirq_domain_ops = {
0118     .translate  = mtk_sysirq_domain_translate,
0119     .alloc      = mtk_sysirq_domain_alloc,
0120     .free       = irq_domain_free_irqs_common,
0121 };
0122 
0123 static int __init mtk_sysirq_of_init(struct device_node *node,
0124                      struct device_node *parent)
0125 {
0126     struct irq_domain *domain, *domain_parent;
0127     struct mtk_sysirq_chip_data *chip_data;
0128     int ret, size, intpol_num = 0, nr_intpol_bases = 0, i = 0;
0129 
0130     domain_parent = irq_find_host(parent);
0131     if (!domain_parent) {
0132         pr_err("mtk_sysirq: interrupt-parent not found\n");
0133         return -EINVAL;
0134     }
0135 
0136     chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
0137     if (!chip_data)
0138         return -ENOMEM;
0139 
0140     while (of_get_address(node, i++, NULL, NULL))
0141         nr_intpol_bases++;
0142 
0143     if (nr_intpol_bases == 0) {
0144         pr_err("mtk_sysirq: base address not specified\n");
0145         ret = -EINVAL;
0146         goto out_free_chip;
0147     }
0148 
0149     chip_data->intpol_words = kcalloc(nr_intpol_bases,
0150                       sizeof(*chip_data->intpol_words),
0151                       GFP_KERNEL);
0152     if (!chip_data->intpol_words) {
0153         ret = -ENOMEM;
0154         goto out_free_chip;
0155     }
0156 
0157     chip_data->intpol_bases = kcalloc(nr_intpol_bases,
0158                       sizeof(*chip_data->intpol_bases),
0159                       GFP_KERNEL);
0160     if (!chip_data->intpol_bases) {
0161         ret = -ENOMEM;
0162         goto out_free_intpol_words;
0163     }
0164 
0165     for (i = 0; i < nr_intpol_bases; i++) {
0166         struct resource res;
0167 
0168         ret = of_address_to_resource(node, i, &res);
0169         size = resource_size(&res);
0170         intpol_num += size * 8;
0171         chip_data->intpol_words[i] = size / 4;
0172         chip_data->intpol_bases[i] = of_iomap(node, i);
0173         if (ret || !chip_data->intpol_bases[i]) {
0174             pr_err("%pOF: couldn't map region %d\n", node, i);
0175             ret = -ENODEV;
0176             goto out_free_intpol;
0177         }
0178     }
0179 
0180     chip_data->intpol_idx = kcalloc(intpol_num,
0181                     sizeof(*chip_data->intpol_idx),
0182                     GFP_KERNEL);
0183     if (!chip_data->intpol_idx) {
0184         ret = -ENOMEM;
0185         goto out_free_intpol;
0186     }
0187 
0188     chip_data->which_word = kcalloc(intpol_num,
0189                     sizeof(*chip_data->which_word),
0190                     GFP_KERNEL);
0191     if (!chip_data->which_word) {
0192         ret = -ENOMEM;
0193         goto out_free_intpol_idx;
0194     }
0195 
0196     /*
0197      * assign an index of the intpol_bases for each irq
0198      * to set it fast later
0199      */
0200     for (i = 0; i < intpol_num ; i++) {
0201         u32 word = i / 32, j;
0202 
0203         for (j = 0; word >= chip_data->intpol_words[j] ; j++)
0204             word -= chip_data->intpol_words[j];
0205 
0206         chip_data->intpol_idx[i] = j;
0207         chip_data->which_word[i] = word;
0208     }
0209 
0210     domain = irq_domain_add_hierarchy(domain_parent, 0, intpol_num, node,
0211                       &sysirq_domain_ops, chip_data);
0212     if (!domain) {
0213         ret = -ENOMEM;
0214         goto out_free_which_word;
0215     }
0216     raw_spin_lock_init(&chip_data->lock);
0217 
0218     return 0;
0219 
0220 out_free_which_word:
0221     kfree(chip_data->which_word);
0222 out_free_intpol_idx:
0223     kfree(chip_data->intpol_idx);
0224 out_free_intpol:
0225     for (i = 0; i < nr_intpol_bases; i++)
0226         if (chip_data->intpol_bases[i])
0227             iounmap(chip_data->intpol_bases[i]);
0228     kfree(chip_data->intpol_bases);
0229 out_free_intpol_words:
0230     kfree(chip_data->intpol_words);
0231 out_free_chip:
0232     kfree(chip_data);
0233     return ret;
0234 }
0235 IRQCHIP_DECLARE(mtk_sysirq, "mediatek,mt6577-sysirq", mtk_sysirq_of_init);