1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Texas Instruments Keystone IRQ controller IP driver 4 * 5 * Copyright (C) 2014 Texas Instruments, Inc. 6 * Author: Sajesh Kumar Saran <sajesh@ti.com> 7 * Grygorii Strashko <grygorii.strashko@ti.com> 8 */ 9 10 #include <linux/irq.h> 11 #include <linux/bitops.h> 12 #include <linux/module.h> 13 #include <linux/moduleparam.h> 14 #include <linux/interrupt.h> 15 #include <linux/irqdomain.h> 16 #include <linux/irqchip.h> 17 #include <linux/of.h> 18 #include <linux/platform_device.h> 19 #include <linux/mfd/syscon.h> 20 #include <linux/regmap.h> 21 22 /* The source ID bits start from 4 to 31 (total 28 bits)*/ 23 #define BIT_OFS 4 24 #define KEYSTONE_N_IRQ (32 - BIT_OFS) 25 26 struct keystone_irq_device { 27 struct device *dev; 28 struct irq_chip chip; 29 u32 mask; 30 int irq; 31 struct irq_domain *irqd; 32 struct regmap *devctrl_regs; 33 u32 devctrl_offset; 34 raw_spinlock_t wa_lock; 35 }; 36 37 static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq) 38 { 39 int ret; 40 u32 val = 0; 41 42 ret = regmap_read(kirq->devctrl_regs, kirq->devctrl_offset, &val); 43 if (ret < 0) 44 dev_dbg(kirq->dev, "irq read failed ret(%d)\n", ret); 45 return val; 46 } 47 48 static inline void 49 keystone_irq_writel(struct keystone_irq_device *kirq, u32 value) 50 { 51 int ret; 52 53 ret = regmap_write(kirq->devctrl_regs, kirq->devctrl_offset, value); 54 if (ret < 0) 55 dev_dbg(kirq->dev, "irq write failed ret(%d)\n", ret); 56 } 57 58 static void keystone_irq_setmask(struct irq_data *d) 59 { 60 struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d); 61 62 kirq->mask |= BIT(d->hwirq); 63 dev_dbg(kirq->dev, "mask %lu [%x]\n", d->hwirq, kirq->mask); 64 } 65 66 static void keystone_irq_unmask(struct irq_data *d) 67 { 68 struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d); 69 70 kirq->mask &= ~BIT(d->hwirq); 71 dev_dbg(kirq->dev, "unmask %lu [%x]\n", d->hwirq, kirq->mask); 72 } 73 74 static void keystone_irq_ack(struct irq_data *d) 75 { 76 /* nothing to do here */ 77 } 78 79 static irqreturn_t keystone_irq_handler(int irq, void *keystone_irq) 80 { 81 struct keystone_irq_device *kirq = keystone_irq; 82 unsigned long wa_lock_flags; 83 unsigned long pending; 84 int src, err; 85 86 dev_dbg(kirq->dev, "start irq %d\n", irq); 87 88 pending = keystone_irq_readl(kirq); 89 keystone_irq_writel(kirq, pending); 90 91 dev_dbg(kirq->dev, "pending 0x%lx, mask 0x%x\n", pending, kirq->mask); 92 93 pending = (pending >> BIT_OFS) & ~kirq->mask; 94 95 dev_dbg(kirq->dev, "pending after mask 0x%lx\n", pending); 96 97 for (src = 0; src < KEYSTONE_N_IRQ; src++) { 98 if (BIT(src) & pending) { 99 raw_spin_lock_irqsave(&kirq->wa_lock, wa_lock_flags); 100 err = generic_handle_domain_irq(kirq->irqd, src); 101 raw_spin_unlock_irqrestore(&kirq->wa_lock, 102 wa_lock_flags); 103 104 if (err) 105 dev_warn_ratelimited(kirq->dev, "spurious irq detected hwirq %d\n", 106 src); 107 } 108 } 109 110 dev_dbg(kirq->dev, "end irq %d\n", irq); 111 return IRQ_HANDLED; 112 } 113 114 static int keystone_irq_map(struct irq_domain *h, unsigned int virq, 115 irq_hw_number_t hw) 116 { 117 struct keystone_irq_device *kirq = h->host_data; 118 119 irq_set_chip_data(virq, kirq); 120 irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq); 121 irq_set_probe(virq); 122 return 0; 123 } 124 125 static const struct irq_domain_ops keystone_irq_ops = { 126 .map = keystone_irq_map, 127 .xlate = irq_domain_xlate_onecell, 128 }; 129 130 static int keystone_irq_probe(struct platform_device *pdev) 131 { 132 struct device *dev = &pdev->dev; 133 struct device_node *np = dev->of_node; 134 struct keystone_irq_device *kirq; 135 int ret; 136 137 if (np == NULL) 138 return -EINVAL; 139 140 kirq = devm_kzalloc(dev, sizeof(*kirq), GFP_KERNEL); 141 if (!kirq) 142 return -ENOMEM; 143 144 kirq->devctrl_regs = 145 syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); 146 if (IS_ERR(kirq->devctrl_regs)) 147 return PTR_ERR(kirq->devctrl_regs); 148 149 ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, 150 &kirq->devctrl_offset); 151 if (ret) { 152 dev_err(dev, "couldn't read the devctrl_offset offset!\n"); 153 return ret; 154 } 155 156 kirq->irq = platform_get_irq(pdev, 0); 157 if (kirq->irq < 0) 158 return kirq->irq; 159 160 kirq->dev = dev; 161 kirq->mask = ~0x0; 162 kirq->chip.name = "keystone-irq"; 163 kirq->chip.irq_ack = keystone_irq_ack; 164 kirq->chip.irq_mask = keystone_irq_setmask; 165 kirq->chip.irq_unmask = keystone_irq_unmask; 166 167 kirq->irqd = irq_domain_add_linear(np, KEYSTONE_N_IRQ, 168 &keystone_irq_ops, kirq); 169 if (!kirq->irqd) { 170 dev_err(dev, "IRQ domain registration failed\n"); 171 return -ENODEV; 172 } 173 174 raw_spin_lock_init(&kirq->wa_lock); 175 176 platform_set_drvdata(pdev, kirq); 177 178 ret = request_irq(kirq->irq, keystone_irq_handler, 179 0, dev_name(dev), kirq); 180 if (ret) { 181 irq_domain_remove(kirq->irqd); 182 return ret; 183 } 184 185 /* clear all source bits */ 186 keystone_irq_writel(kirq, ~0x0); 187 188 dev_info(dev, "irqchip registered, nr_irqs %u\n", KEYSTONE_N_IRQ); 189 190 return 0; 191 } 192 193 static void keystone_irq_remove(struct platform_device *pdev) 194 { 195 struct keystone_irq_device *kirq = platform_get_drvdata(pdev); 196 int hwirq; 197 198 free_irq(kirq->irq, kirq); 199 200 for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++) 201 irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq)); 202 203 irq_domain_remove(kirq->irqd); 204 } 205 206 static const struct of_device_id keystone_irq_dt_ids[] = { 207 { .compatible = "ti,keystone-irq", }, 208 {}, 209 }; 210 MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids); 211 212 static struct platform_driver keystone_irq_device_driver = { 213 .probe = keystone_irq_probe, 214 .remove_new = keystone_irq_remove, 215 .driver = { 216 .name = "keystone_irq", 217 .of_match_table = of_match_ptr(keystone_irq_dt_ids), 218 } 219 }; 220 221 module_platform_driver(keystone_irq_device_driver); 222 223 MODULE_AUTHOR("Texas Instruments"); 224 MODULE_AUTHOR("Sajesh Kumar Saran"); 225 MODULE_AUTHOR("Grygorii Strashko"); 226 MODULE_DESCRIPTION("Keystone IRQ chip"); 227 MODULE_LICENSE("GPL v2"); 228