1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Interrupt controller for the 4 * Communication Processor Module. 5 * Copyright (c) 1997 Dan error_act (dmalek@jlc.net) 6 */ 7 #include <linux/kernel.h> 8 #include <linux/interrupt.h> 9 #include <linux/irqdomain.h> 10 #include <linux/platform_device.h> 11 #include <asm/cpm1.h> 12 13 struct cpm_pic_data { 14 cpic8xx_t __iomem *reg; 15 struct irq_domain *host; 16 }; 17 18 static void cpm_mask_irq(struct irq_data *d) 19 { 20 struct cpm_pic_data *data = irq_data_get_irq_chip_data(d); 21 unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d); 22 23 clrbits32(&data->reg->cpic_cimr, (1 << cpm_vec)); 24 } 25 26 static void cpm_unmask_irq(struct irq_data *d) 27 { 28 struct cpm_pic_data *data = irq_data_get_irq_chip_data(d); 29 unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d); 30 31 setbits32(&data->reg->cpic_cimr, (1 << cpm_vec)); 32 } 33 34 static void cpm_end_irq(struct irq_data *d) 35 { 36 struct cpm_pic_data *data = irq_data_get_irq_chip_data(d); 37 unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d); 38 39 out_be32(&data->reg->cpic_cisr, (1 << cpm_vec)); 40 } 41 42 static struct irq_chip cpm_pic = { 43 .name = "CPM PIC", 44 .irq_mask = cpm_mask_irq, 45 .irq_unmask = cpm_unmask_irq, 46 .irq_eoi = cpm_end_irq, 47 }; 48 49 static int cpm_get_irq(struct irq_desc *desc) 50 { 51 struct cpm_pic_data *data = irq_desc_get_handler_data(desc); 52 int cpm_vec; 53 54 /* 55 * Get the vector by setting the ACK bit and then reading 56 * the register. 57 */ 58 out_be16(&data->reg->cpic_civr, 1); 59 cpm_vec = in_be16(&data->reg->cpic_civr); 60 cpm_vec >>= 11; 61 62 return irq_linear_revmap(data->host, cpm_vec); 63 } 64 65 static void cpm_cascade(struct irq_desc *desc) 66 { 67 generic_handle_irq(cpm_get_irq(desc)); 68 } 69 70 static int cpm_pic_host_map(struct irq_domain *h, unsigned int virq, 71 irq_hw_number_t hw) 72 { 73 irq_set_chip_data(virq, h->host_data); 74 irq_set_status_flags(virq, IRQ_LEVEL); 75 irq_set_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq); 76 return 0; 77 } 78 79 static const struct irq_domain_ops cpm_pic_host_ops = { 80 .map = cpm_pic_host_map, 81 }; 82 83 static int cpm_pic_probe(struct platform_device *pdev) 84 { 85 struct device *dev = &pdev->dev; 86 struct resource *res; 87 int irq; 88 struct cpm_pic_data *data; 89 90 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 91 if (!res) 92 return -ENODEV; 93 94 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 95 if (!data) 96 return -ENOMEM; 97 98 data->reg = devm_ioremap(dev, res->start, resource_size(res)); 99 if (!data->reg) 100 return -ENODEV; 101 102 irq = platform_get_irq(pdev, 0); 103 if (irq < 0) 104 return irq; 105 106 /* Initialize the CPM interrupt controller. */ 107 out_be32(&data->reg->cpic_cicr, 108 (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | 109 ((virq_to_hw(irq) / 2) << 13) | CICR_HP_MASK); 110 111 out_be32(&data->reg->cpic_cimr, 0); 112 113 data->host = irq_domain_add_linear(dev->of_node, 64, &cpm_pic_host_ops, data); 114 if (!data->host) 115 return -ENODEV; 116 117 irq_set_handler_data(irq, data); 118 irq_set_chained_handler(irq, cpm_cascade); 119 120 setbits32(&data->reg->cpic_cicr, CICR_IEN); 121 122 return 0; 123 } 124 125 static const struct of_device_id cpm_pic_match[] = { 126 { 127 .compatible = "fsl,cpm1-pic", 128 }, { 129 .type = "cpm-pic", 130 .compatible = "CPM", 131 }, {}, 132 }; 133 134 static struct platform_driver cpm_pic_driver = { 135 .driver = { 136 .name = "cpm-pic", 137 .of_match_table = cpm_pic_match, 138 }, 139 .probe = cpm_pic_probe, 140 }; 141 142 static int __init cpm_pic_init(void) 143 { 144 return platform_driver_register(&cpm_pic_driver); 145 } 146 arch_initcall(cpm_pic_init); 147 148 /* 149 * The CPM can generate the error interrupt when there is a race condition 150 * between generating and masking interrupts. All we have to do is ACK it 151 * and return. This is a no-op function so we don't need any special 152 * tests in the interrupt handler. 153 */ 154 static irqreturn_t cpm_error_interrupt(int irq, void *dev) 155 { 156 return IRQ_HANDLED; 157 } 158 159 static int cpm_error_probe(struct platform_device *pdev) 160 { 161 int irq; 162 163 irq = platform_get_irq(pdev, 0); 164 if (irq < 0) 165 return irq; 166 167 return request_irq(irq, cpm_error_interrupt, IRQF_NO_THREAD, "error", NULL); 168 } 169 170 static const struct of_device_id cpm_error_ids[] = { 171 { .compatible = "fsl,cpm1" }, 172 { .type = "cpm" }, 173 {}, 174 }; 175 176 static struct platform_driver cpm_error_driver = { 177 .driver = { 178 .name = "cpm-error", 179 .of_match_table = cpm_error_ids, 180 }, 181 .probe = cpm_error_probe, 182 }; 183 184 static int __init cpm_error_init(void) 185 { 186 return platform_driver_register(&cpm_error_driver); 187 } 188 subsys_initcall(cpm_error_init); 189