Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.16-rc1 274 lines 8.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Driver for the Microchip LAN966x outbound interrupt controller 4 * 5 * Copyright (c) 2024 Technology Inc. and its subsidiaries. 6 * 7 * Authors: 8 * Horatiu Vultur <horatiu.vultur@microchip.com> 9 * Clément Léger <clement.leger@bootlin.com> 10 * Herve Codina <herve.codina@bootlin.com> 11 */ 12 13#include <linux/interrupt.h> 14#include <linux/irqchip/chained_irq.h> 15#include <linux/irqchip.h> 16#include <linux/irq.h> 17#include <linux/mod_devicetable.h> 18#include <linux/module.h> 19#include <linux/platform_device.h> 20#include <linux/slab.h> 21 22struct lan966x_oic_chip_regs { 23 int reg_off_ena_set; 24 int reg_off_ena_clr; 25 int reg_off_sticky; 26 int reg_off_ident; 27 int reg_off_map; 28}; 29 30struct lan966x_oic_data { 31 void __iomem *regs; 32 int irq; 33}; 34 35#define LAN966X_OIC_NR_IRQ 86 36 37/* Interrupt sticky status */ 38#define LAN966X_OIC_INTR_STICKY 0x30 39#define LAN966X_OIC_INTR_STICKY1 0x34 40#define LAN966X_OIC_INTR_STICKY2 0x38 41 42/* Interrupt enable */ 43#define LAN966X_OIC_INTR_ENA 0x48 44#define LAN966X_OIC_INTR_ENA1 0x4c 45#define LAN966X_OIC_INTR_ENA2 0x50 46 47/* Atomic clear of interrupt enable */ 48#define LAN966X_OIC_INTR_ENA_CLR 0x54 49#define LAN966X_OIC_INTR_ENA_CLR1 0x58 50#define LAN966X_OIC_INTR_ENA_CLR2 0x5c 51 52/* Atomic set of interrupt */ 53#define LAN966X_OIC_INTR_ENA_SET 0x60 54#define LAN966X_OIC_INTR_ENA_SET1 0x64 55#define LAN966X_OIC_INTR_ENA_SET2 0x68 56 57/* Mapping of source to destination interrupts (_n = 0..8) */ 58#define LAN966X_OIC_DST_INTR_MAP(_n) (0x78 + (_n) * 4) 59#define LAN966X_OIC_DST_INTR_MAP1(_n) (0x9c + (_n) * 4) 60#define LAN966X_OIC_DST_INTR_MAP2(_n) (0xc0 + (_n) * 4) 61 62/* Currently active interrupt sources per destination (_n = 0..8) */ 63#define LAN966X_OIC_DST_INTR_IDENT(_n) (0xe4 + (_n) * 4) 64#define LAN966X_OIC_DST_INTR_IDENT1(_n) (0x108 + (_n) * 4) 65#define LAN966X_OIC_DST_INTR_IDENT2(_n) (0x12c + (_n) * 4) 66 67static unsigned int lan966x_oic_irq_startup(struct irq_data *data) 68{ 69 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 70 struct irq_chip_type *ct = irq_data_get_chip_type(data); 71 struct lan966x_oic_chip_regs *chip_regs = gc->private; 72 u32 map; 73 74 scoped_guard (raw_spinlock, &gc->lock) { 75 /* Map the source interrupt to the destination */ 76 map = irq_reg_readl(gc, chip_regs->reg_off_map); 77 map |= data->mask; 78 irq_reg_writel(gc, map, chip_regs->reg_off_map); 79 } 80 81 ct->chip.irq_ack(data); 82 ct->chip.irq_unmask(data); 83 84 return 0; 85} 86 87static void lan966x_oic_irq_shutdown(struct irq_data *data) 88{ 89 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 90 struct irq_chip_type *ct = irq_data_get_chip_type(data); 91 struct lan966x_oic_chip_regs *chip_regs = gc->private; 92 u32 map; 93 94 ct->chip.irq_mask(data); 95 96 guard(raw_spinlock)(&gc->lock); 97 98 /* Unmap the interrupt */ 99 map = irq_reg_readl(gc, chip_regs->reg_off_map); 100 map &= ~data->mask; 101 irq_reg_writel(gc, map, chip_regs->reg_off_map); 102} 103 104static int lan966x_oic_irq_set_type(struct irq_data *data, 105 unsigned int flow_type) 106{ 107 if (flow_type != IRQ_TYPE_LEVEL_HIGH) { 108 pr_err("lan966x oic doesn't support flow type %d\n", flow_type); 109 return -EINVAL; 110 } 111 112 return 0; 113} 114 115static void lan966x_oic_irq_handler_domain(struct irq_domain *d, u32 first_irq) 116{ 117 struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, first_irq); 118 struct lan966x_oic_chip_regs *chip_regs = gc->private; 119 unsigned long ident; 120 unsigned int hwirq; 121 122 ident = irq_reg_readl(gc, chip_regs->reg_off_ident); 123 if (!ident) 124 return; 125 126 for_each_set_bit(hwirq, &ident, 32) 127 generic_handle_domain_irq(d, hwirq + first_irq); 128} 129 130static void lan966x_oic_irq_handler(struct irq_desc *desc) 131{ 132 struct irq_domain *d = irq_desc_get_handler_data(desc); 133 struct irq_chip *chip = irq_desc_get_chip(desc); 134 135 chained_irq_enter(chip, desc); 136 lan966x_oic_irq_handler_domain(d, 0); 137 lan966x_oic_irq_handler_domain(d, 32); 138 lan966x_oic_irq_handler_domain(d, 64); 139 chained_irq_exit(chip, desc); 140} 141 142static struct lan966x_oic_chip_regs lan966x_oic_chip_regs[3] = { 143 { 144 .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET, 145 .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR, 146 .reg_off_sticky = LAN966X_OIC_INTR_STICKY, 147 .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT(0), 148 .reg_off_map = LAN966X_OIC_DST_INTR_MAP(0), 149 }, { 150 .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET1, 151 .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR1, 152 .reg_off_sticky = LAN966X_OIC_INTR_STICKY1, 153 .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT1(0), 154 .reg_off_map = LAN966X_OIC_DST_INTR_MAP1(0), 155 }, { 156 .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET2, 157 .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR2, 158 .reg_off_sticky = LAN966X_OIC_INTR_STICKY2, 159 .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT2(0), 160 .reg_off_map = LAN966X_OIC_DST_INTR_MAP2(0), 161 } 162}; 163 164static int lan966x_oic_chip_init(struct irq_chip_generic *gc) 165{ 166 struct lan966x_oic_data *lan966x_oic = gc->domain->host_data; 167 struct lan966x_oic_chip_regs *chip_regs; 168 169 chip_regs = &lan966x_oic_chip_regs[gc->irq_base / 32]; 170 171 gc->reg_base = lan966x_oic->regs; 172 gc->chip_types[0].regs.enable = chip_regs->reg_off_ena_set; 173 gc->chip_types[0].regs.disable = chip_regs->reg_off_ena_clr; 174 gc->chip_types[0].regs.ack = chip_regs->reg_off_sticky; 175 gc->chip_types[0].chip.irq_startup = lan966x_oic_irq_startup; 176 gc->chip_types[0].chip.irq_shutdown = lan966x_oic_irq_shutdown; 177 gc->chip_types[0].chip.irq_set_type = lan966x_oic_irq_set_type; 178 gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; 179 gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; 180 gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 181 gc->private = chip_regs; 182 183 /* Disable all interrupts handled by this chip */ 184 irq_reg_writel(gc, ~0U, chip_regs->reg_off_ena_clr); 185 186 return 0; 187} 188 189static void lan966x_oic_chip_exit(struct irq_chip_generic *gc) 190{ 191 /* Disable and ack all interrupts handled by this chip */ 192 irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.disable); 193 irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.ack); 194} 195 196static int lan966x_oic_domain_init(struct irq_domain *d) 197{ 198 struct lan966x_oic_data *lan966x_oic = d->host_data; 199 200 irq_set_chained_handler_and_data(lan966x_oic->irq, lan966x_oic_irq_handler, d); 201 202 return 0; 203} 204 205static void lan966x_oic_domain_exit(struct irq_domain *d) 206{ 207 struct lan966x_oic_data *lan966x_oic = d->host_data; 208 209 irq_set_chained_handler_and_data(lan966x_oic->irq, NULL, NULL); 210} 211 212static int lan966x_oic_probe(struct platform_device *pdev) 213{ 214 struct irq_domain_chip_generic_info dgc_info = { 215 .name = "lan966x-oic", 216 .handler = handle_level_irq, 217 .irqs_per_chip = 32, 218 .num_ct = 1, 219 .init = lan966x_oic_chip_init, 220 .exit = lan966x_oic_chip_exit, 221 }; 222 struct irq_domain_info d_info = { 223 .fwnode = of_fwnode_handle(pdev->dev.of_node), 224 .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC, 225 .size = LAN966X_OIC_NR_IRQ, 226 .hwirq_max = LAN966X_OIC_NR_IRQ, 227 .ops = &irq_generic_chip_ops, 228 .dgc_info = &dgc_info, 229 .init = lan966x_oic_domain_init, 230 .exit = lan966x_oic_domain_exit, 231 }; 232 struct lan966x_oic_data *lan966x_oic; 233 struct device *dev = &pdev->dev; 234 struct irq_domain *domain; 235 236 lan966x_oic = devm_kmalloc(dev, sizeof(*lan966x_oic), GFP_KERNEL); 237 if (!lan966x_oic) 238 return -ENOMEM; 239 240 lan966x_oic->regs = devm_platform_ioremap_resource(pdev, 0); 241 if (IS_ERR(lan966x_oic->regs)) 242 return dev_err_probe(dev, PTR_ERR(lan966x_oic->regs), 243 "failed to map resource\n"); 244 245 lan966x_oic->irq = platform_get_irq(pdev, 0); 246 if (lan966x_oic->irq < 0) 247 return dev_err_probe(dev, lan966x_oic->irq, "failed to get the IRQ\n"); 248 249 d_info.host_data = lan966x_oic; 250 domain = devm_irq_domain_instantiate(dev, &d_info); 251 if (IS_ERR(domain)) 252 return dev_err_probe(dev, PTR_ERR(domain), 253 "failed to instantiate the IRQ domain\n"); 254 return 0; 255} 256 257static const struct of_device_id lan966x_oic_of_match[] = { 258 { .compatible = "microchip,lan966x-oic" }, 259 {} /* sentinel */ 260}; 261MODULE_DEVICE_TABLE(of, lan966x_oic_of_match); 262 263static struct platform_driver lan966x_oic_driver = { 264 .probe = lan966x_oic_probe, 265 .driver = { 266 .name = "lan966x-oic", 267 .of_match_table = lan966x_oic_of_match, 268 }, 269}; 270module_platform_driver(lan966x_oic_driver); 271 272MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); 273MODULE_DESCRIPTION("Microchip LAN966x OIC driver"); 274MODULE_LICENSE("GPL");