cpm1-ic.c (4337B)
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 13struct cpm_pic_data { 14 cpic8xx_t __iomem *reg; 15 struct irq_domain *host; 16}; 17 18static 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 26static 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 34static 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 42static 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 49static 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 65static void cpm_cascade(struct irq_desc *desc) 66{ 67 generic_handle_irq(cpm_get_irq(desc)); 68} 69 70static 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 79static const struct irq_domain_ops cpm_pic_host_ops = { 80 .map = cpm_pic_host_map, 81}; 82 83static 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 125static 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 134static 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 142static int __init cpm_pic_init(void) 143{ 144 return platform_driver_register(&cpm_pic_driver); 145} 146arch_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 */ 154static irqreturn_t cpm_error_interrupt(int irq, void *dev) 155{ 156 return IRQ_HANDLED; 157} 158 159static 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 170static const struct of_device_id cpm_error_ids[] = { 171 { .compatible = "fsl,cpm1" }, 172 { .type = "cpm" }, 173 {}, 174}; 175 176static 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 184static int __init cpm_error_init(void) 185{ 186 return platform_driver_register(&cpm_error_driver); 187} 188subsys_initcall(cpm_error_init);