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_find_mapping(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_create_linear(of_fwnode_handle(dev->of_node), 114 64, &cpm_pic_host_ops, data); 115 if (!data->host) 116 return -ENODEV; 117 118 irq_set_handler_data(irq, data); 119 irq_set_chained_handler(irq, cpm_cascade); 120 121 setbits32(&data->reg->cpic_cicr, CICR_IEN); 122 123 return 0; 124 } 125 126 static const struct of_device_id cpm_pic_match[] = { 127 { 128 .compatible = "fsl,cpm1-pic", 129 }, { 130 .type = "cpm-pic", 131 .compatible = "CPM", 132 }, {}, 133 }; 134 135 static struct platform_driver cpm_pic_driver = { 136 .driver = { 137 .name = "cpm-pic", 138 .of_match_table = cpm_pic_match, 139 }, 140 .probe = cpm_pic_probe, 141 }; 142 143 static int __init cpm_pic_init(void) 144 { 145 return platform_driver_register(&cpm_pic_driver); 146 } 147 arch_initcall(cpm_pic_init); 148 149 /* 150 * The CPM can generate the error interrupt when there is a race condition 151 * between generating and masking interrupts. All we have to do is ACK it 152 * and return. This is a no-op function so we don't need any special 153 * tests in the interrupt handler. 154 */ 155 static irqreturn_t cpm_error_interrupt(int irq, void *dev) 156 { 157 return IRQ_HANDLED; 158 } 159 160 static int cpm_error_probe(struct platform_device *pdev) 161 { 162 int irq; 163 164 irq = platform_get_irq(pdev, 0); 165 if (irq < 0) 166 return irq; 167 168 return request_irq(irq, cpm_error_interrupt, IRQF_NO_THREAD, "error", NULL); 169 } 170 171 static const struct of_device_id cpm_error_ids[] = { 172 { .compatible = "fsl,cpm1" }, 173 { .type = "cpm" }, 174 {}, 175 }; 176 177 static struct platform_driver cpm_error_driver = { 178 .driver = { 179 .name = "cpm-error", 180 .of_match_table = cpm_error_ids, 181 }, 182 .probe = cpm_error_probe, 183 }; 184 185 static int __init cpm_error_init(void) 186 { 187 return platform_driver_register(&cpm_error_driver); 188 } 189 subsys_initcall(cpm_error_init); 190