Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Interrupt controller for the
0004  * Communication Processor Module.
0005  * Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
0006  */
0007 #include <linux/kernel.h>
0008 #include <linux/interrupt.h>
0009 #include <linux/irqdomain.h>
0010 #include <linux/platform_device.h>
0011 #include <asm/cpm1.h>
0012 
0013 struct cpm_pic_data {
0014     cpic8xx_t __iomem *reg;
0015     struct irq_domain *host;
0016 };
0017 
0018 static void cpm_mask_irq(struct irq_data *d)
0019 {
0020     struct cpm_pic_data *data = irq_data_get_irq_chip_data(d);
0021     unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
0022 
0023     clrbits32(&data->reg->cpic_cimr, (1 << cpm_vec));
0024 }
0025 
0026 static void cpm_unmask_irq(struct irq_data *d)
0027 {
0028     struct cpm_pic_data *data = irq_data_get_irq_chip_data(d);
0029     unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
0030 
0031     setbits32(&data->reg->cpic_cimr, (1 << cpm_vec));
0032 }
0033 
0034 static void cpm_end_irq(struct irq_data *d)
0035 {
0036     struct cpm_pic_data *data = irq_data_get_irq_chip_data(d);
0037     unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
0038 
0039     out_be32(&data->reg->cpic_cisr, (1 << cpm_vec));
0040 }
0041 
0042 static struct irq_chip cpm_pic = {
0043     .name = "CPM PIC",
0044     .irq_mask = cpm_mask_irq,
0045     .irq_unmask = cpm_unmask_irq,
0046     .irq_eoi = cpm_end_irq,
0047 };
0048 
0049 static int cpm_get_irq(struct irq_desc *desc)
0050 {
0051     struct cpm_pic_data *data = irq_desc_get_handler_data(desc);
0052     int cpm_vec;
0053 
0054     /*
0055      * Get the vector by setting the ACK bit and then reading
0056      * the register.
0057      */
0058     out_be16(&data->reg->cpic_civr, 1);
0059     cpm_vec = in_be16(&data->reg->cpic_civr);
0060     cpm_vec >>= 11;
0061 
0062     return irq_linear_revmap(data->host, cpm_vec);
0063 }
0064 
0065 static void cpm_cascade(struct irq_desc *desc)
0066 {
0067     generic_handle_irq(cpm_get_irq(desc));
0068 }
0069 
0070 static int cpm_pic_host_map(struct irq_domain *h, unsigned int virq,
0071                 irq_hw_number_t hw)
0072 {
0073     irq_set_chip_data(virq, h->host_data);
0074     irq_set_status_flags(virq, IRQ_LEVEL);
0075     irq_set_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq);
0076     return 0;
0077 }
0078 
0079 static const struct irq_domain_ops cpm_pic_host_ops = {
0080     .map = cpm_pic_host_map,
0081 };
0082 
0083 static int cpm_pic_probe(struct platform_device *pdev)
0084 {
0085     struct device *dev = &pdev->dev;
0086     struct resource *res;
0087     int irq;
0088     struct cpm_pic_data *data;
0089 
0090     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0091     if (!res)
0092         return -ENODEV;
0093 
0094     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0095     if (!data)
0096         return -ENOMEM;
0097 
0098     data->reg = devm_ioremap(dev, res->start, resource_size(res));
0099     if (!data->reg)
0100         return -ENODEV;
0101 
0102     irq = platform_get_irq(pdev, 0);
0103     if (irq < 0)
0104         return irq;
0105 
0106     /* Initialize the CPM interrupt controller. */
0107     out_be32(&data->reg->cpic_cicr,
0108          (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
0109          ((virq_to_hw(irq) / 2) << 13) | CICR_HP_MASK);
0110 
0111     out_be32(&data->reg->cpic_cimr, 0);
0112 
0113     data->host = irq_domain_add_linear(dev->of_node, 64, &cpm_pic_host_ops, data);
0114     if (!data->host)
0115         return -ENODEV;
0116 
0117     irq_set_handler_data(irq, data);
0118     irq_set_chained_handler(irq, cpm_cascade);
0119 
0120     setbits32(&data->reg->cpic_cicr, CICR_IEN);
0121 
0122     return 0;
0123 }
0124 
0125 static const struct of_device_id cpm_pic_match[] = {
0126     {
0127         .compatible = "fsl,cpm1-pic",
0128     }, {
0129         .type = "cpm-pic",
0130         .compatible = "CPM",
0131     }, {},
0132 };
0133 
0134 static struct platform_driver cpm_pic_driver = {
0135     .driver = {
0136         .name       = "cpm-pic",
0137         .of_match_table = cpm_pic_match,
0138     },
0139     .probe  = cpm_pic_probe,
0140 };
0141 
0142 static int __init cpm_pic_init(void)
0143 {
0144     return platform_driver_register(&cpm_pic_driver);
0145 }
0146 arch_initcall(cpm_pic_init);
0147 
0148 /*
0149  * The CPM can generate the error interrupt when there is a race condition
0150  * between generating and masking interrupts.  All we have to do is ACK it
0151  * and return.  This is a no-op function so we don't need any special
0152  * tests in the interrupt handler.
0153  */
0154 static irqreturn_t cpm_error_interrupt(int irq, void *dev)
0155 {
0156     return IRQ_HANDLED;
0157 }
0158 
0159 static int cpm_error_probe(struct platform_device *pdev)
0160 {
0161     int irq;
0162 
0163     irq = platform_get_irq(pdev, 0);
0164     if (irq < 0)
0165         return irq;
0166 
0167     return request_irq(irq, cpm_error_interrupt, IRQF_NO_THREAD, "error", NULL);
0168 }
0169 
0170 static const struct of_device_id cpm_error_ids[] = {
0171     { .compatible = "fsl,cpm1" },
0172     { .type = "cpm" },
0173     {},
0174 };
0175 
0176 static struct platform_driver cpm_error_driver = {
0177     .driver = {
0178         .name       = "cpm-error",
0179         .of_match_table = cpm_error_ids,
0180     },
0181     .probe  = cpm_error_probe,
0182 };
0183 
0184 static int __init cpm_error_init(void)
0185 {
0186     return platform_driver_register(&cpm_error_driver);
0187 }
0188 subsys_initcall(cpm_error_init);