11eb77c3bSTalel Shenhar // SPDX-License-Identifier: GPL-2.0 21eb77c3bSTalel Shenhar /* 31eb77c3bSTalel Shenhar * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 41eb77c3bSTalel Shenhar */ 51eb77c3bSTalel Shenhar 61eb77c3bSTalel Shenhar #include <linux/bitfield.h> 71eb77c3bSTalel Shenhar #include <linux/irq.h> 81eb77c3bSTalel Shenhar #include <linux/irqchip.h> 91eb77c3bSTalel Shenhar #include <linux/irqchip/chained_irq.h> 101eb77c3bSTalel Shenhar #include <linux/irqdomain.h> 111eb77c3bSTalel Shenhar #include <linux/module.h> 121eb77c3bSTalel Shenhar #include <linux/of.h> 131eb77c3bSTalel Shenhar #include <linux/of_address.h> 141eb77c3bSTalel Shenhar #include <linux/of_irq.h> 151eb77c3bSTalel Shenhar 161eb77c3bSTalel Shenhar /* FIC Registers */ 171eb77c3bSTalel Shenhar #define AL_FIC_CAUSE 0x00 189c426b77STalel Shenhar #define AL_FIC_SET_CAUSE 0x08 191eb77c3bSTalel Shenhar #define AL_FIC_MASK 0x10 201eb77c3bSTalel Shenhar #define AL_FIC_CONTROL 0x28 211eb77c3bSTalel Shenhar 221eb77c3bSTalel Shenhar #define CONTROL_TRIGGER_RISING BIT(3) 231eb77c3bSTalel Shenhar #define CONTROL_MASK_MSI_X BIT(5) 241eb77c3bSTalel Shenhar 251eb77c3bSTalel Shenhar #define NR_FIC_IRQS 32 261eb77c3bSTalel Shenhar 271eb77c3bSTalel Shenhar MODULE_AUTHOR("Talel Shenhar"); 281eb77c3bSTalel Shenhar MODULE_DESCRIPTION("Amazon's Annapurna Labs Interrupt Controller Driver"); 291eb77c3bSTalel Shenhar MODULE_LICENSE("GPL v2"); 301eb77c3bSTalel Shenhar 311eb77c3bSTalel Shenhar enum al_fic_state { 321eb77c3bSTalel Shenhar AL_FIC_UNCONFIGURED = 0, 331eb77c3bSTalel Shenhar AL_FIC_CONFIGURED_LEVEL, 341eb77c3bSTalel Shenhar AL_FIC_CONFIGURED_RISING_EDGE, 351eb77c3bSTalel Shenhar }; 361eb77c3bSTalel Shenhar 371eb77c3bSTalel Shenhar struct al_fic { 381eb77c3bSTalel Shenhar void __iomem *base; 391eb77c3bSTalel Shenhar struct irq_domain *domain; 401eb77c3bSTalel Shenhar const char *name; 411eb77c3bSTalel Shenhar unsigned int parent_irq; 421eb77c3bSTalel Shenhar enum al_fic_state state; 431eb77c3bSTalel Shenhar }; 441eb77c3bSTalel Shenhar 451eb77c3bSTalel Shenhar static void al_fic_set_trigger(struct al_fic *fic, 461eb77c3bSTalel Shenhar struct irq_chip_generic *gc, 471eb77c3bSTalel Shenhar enum al_fic_state new_state) 481eb77c3bSTalel Shenhar { 491eb77c3bSTalel Shenhar irq_flow_handler_t handler; 501eb77c3bSTalel Shenhar u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL); 511eb77c3bSTalel Shenhar 521eb77c3bSTalel Shenhar if (new_state == AL_FIC_CONFIGURED_LEVEL) { 531eb77c3bSTalel Shenhar handler = handle_level_irq; 541eb77c3bSTalel Shenhar control &= ~CONTROL_TRIGGER_RISING; 551eb77c3bSTalel Shenhar } else { 561eb77c3bSTalel Shenhar handler = handle_edge_irq; 571eb77c3bSTalel Shenhar control |= CONTROL_TRIGGER_RISING; 581eb77c3bSTalel Shenhar } 591eb77c3bSTalel Shenhar gc->chip_types->handler = handler; 601eb77c3bSTalel Shenhar fic->state = new_state; 611eb77c3bSTalel Shenhar writel_relaxed(control, fic->base + AL_FIC_CONTROL); 621eb77c3bSTalel Shenhar } 631eb77c3bSTalel Shenhar 641eb77c3bSTalel Shenhar static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) 651eb77c3bSTalel Shenhar { 661eb77c3bSTalel Shenhar struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 671eb77c3bSTalel Shenhar struct al_fic *fic = gc->private; 681eb77c3bSTalel Shenhar enum al_fic_state new_state; 691eb77c3bSTalel Shenhar int ret = 0; 701eb77c3bSTalel Shenhar 711eb77c3bSTalel Shenhar irq_gc_lock(gc); 721eb77c3bSTalel Shenhar 731eb77c3bSTalel Shenhar if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) && 741eb77c3bSTalel Shenhar ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) { 751eb77c3bSTalel Shenhar pr_debug("fic doesn't support flow type %d\n", flow_type); 761eb77c3bSTalel Shenhar ret = -EINVAL; 771eb77c3bSTalel Shenhar goto err; 781eb77c3bSTalel Shenhar } 791eb77c3bSTalel Shenhar 801eb77c3bSTalel Shenhar new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ? 811eb77c3bSTalel Shenhar AL_FIC_CONFIGURED_LEVEL : AL_FIC_CONFIGURED_RISING_EDGE; 821eb77c3bSTalel Shenhar 831eb77c3bSTalel Shenhar /* 841eb77c3bSTalel Shenhar * A given FIC instance can be either all level or all edge triggered. 851eb77c3bSTalel Shenhar * This is generally fixed depending on what pieces of HW it's wired up 861eb77c3bSTalel Shenhar * to. 871eb77c3bSTalel Shenhar * 881eb77c3bSTalel Shenhar * We configure it based on the sensitivity of the first source 891eb77c3bSTalel Shenhar * being setup, and reject any subsequent attempt at configuring it in a 901eb77c3bSTalel Shenhar * different way. 911eb77c3bSTalel Shenhar */ 921eb77c3bSTalel Shenhar if (fic->state == AL_FIC_UNCONFIGURED) { 931eb77c3bSTalel Shenhar al_fic_set_trigger(fic, gc, new_state); 941eb77c3bSTalel Shenhar } else if (fic->state != new_state) { 951eb77c3bSTalel Shenhar pr_debug("fic %s state already configured to %d\n", 961eb77c3bSTalel Shenhar fic->name, fic->state); 971eb77c3bSTalel Shenhar ret = -EINVAL; 981eb77c3bSTalel Shenhar goto err; 991eb77c3bSTalel Shenhar } 1001eb77c3bSTalel Shenhar 1011eb77c3bSTalel Shenhar err: 1021eb77c3bSTalel Shenhar irq_gc_unlock(gc); 1031eb77c3bSTalel Shenhar 1041eb77c3bSTalel Shenhar return ret; 1051eb77c3bSTalel Shenhar } 1061eb77c3bSTalel Shenhar 1071eb77c3bSTalel Shenhar static void al_fic_irq_handler(struct irq_desc *desc) 1081eb77c3bSTalel Shenhar { 1091eb77c3bSTalel Shenhar struct al_fic *fic = irq_desc_get_handler_data(desc); 1101eb77c3bSTalel Shenhar struct irq_domain *domain = fic->domain; 1111eb77c3bSTalel Shenhar struct irq_chip *irqchip = irq_desc_get_chip(desc); 1121eb77c3bSTalel Shenhar struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); 1131eb77c3bSTalel Shenhar unsigned long pending; 1141eb77c3bSTalel Shenhar u32 hwirq; 1151eb77c3bSTalel Shenhar 1161eb77c3bSTalel Shenhar chained_irq_enter(irqchip, desc); 1171eb77c3bSTalel Shenhar 1181eb77c3bSTalel Shenhar pending = readl_relaxed(fic->base + AL_FIC_CAUSE); 1191eb77c3bSTalel Shenhar pending &= ~gc->mask_cache; 1201eb77c3bSTalel Shenhar 121*046a6ee2SMarc Zyngier for_each_set_bit(hwirq, &pending, NR_FIC_IRQS) 122*046a6ee2SMarc Zyngier generic_handle_domain_irq(domain, hwirq); 1231eb77c3bSTalel Shenhar 1241eb77c3bSTalel Shenhar chained_irq_exit(irqchip, desc); 1251eb77c3bSTalel Shenhar } 1261eb77c3bSTalel Shenhar 1279c426b77STalel Shenhar static int al_fic_irq_retrigger(struct irq_data *data) 1289c426b77STalel Shenhar { 1299c426b77STalel Shenhar struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 1309c426b77STalel Shenhar struct al_fic *fic = gc->private; 1319c426b77STalel Shenhar 1329c426b77STalel Shenhar writel_relaxed(BIT(data->hwirq), fic->base + AL_FIC_SET_CAUSE); 1339c426b77STalel Shenhar 1349c426b77STalel Shenhar return 1; 1359c426b77STalel Shenhar } 1369c426b77STalel Shenhar 1371eb77c3bSTalel Shenhar static int al_fic_register(struct device_node *node, 1381eb77c3bSTalel Shenhar struct al_fic *fic) 1391eb77c3bSTalel Shenhar { 1401eb77c3bSTalel Shenhar struct irq_chip_generic *gc; 1411eb77c3bSTalel Shenhar int ret; 1421eb77c3bSTalel Shenhar 1431eb77c3bSTalel Shenhar fic->domain = irq_domain_add_linear(node, 1441eb77c3bSTalel Shenhar NR_FIC_IRQS, 1451eb77c3bSTalel Shenhar &irq_generic_chip_ops, 1461eb77c3bSTalel Shenhar fic); 1471eb77c3bSTalel Shenhar if (!fic->domain) { 1481eb77c3bSTalel Shenhar pr_err("fail to add irq domain\n"); 1491eb77c3bSTalel Shenhar return -ENOMEM; 1501eb77c3bSTalel Shenhar } 1511eb77c3bSTalel Shenhar 1521eb77c3bSTalel Shenhar ret = irq_alloc_domain_generic_chips(fic->domain, 1531eb77c3bSTalel Shenhar NR_FIC_IRQS, 1541eb77c3bSTalel Shenhar 1, fic->name, 1551eb77c3bSTalel Shenhar handle_level_irq, 1561eb77c3bSTalel Shenhar 0, 0, IRQ_GC_INIT_MASK_CACHE); 1571eb77c3bSTalel Shenhar if (ret) { 1581eb77c3bSTalel Shenhar pr_err("fail to allocate generic chip (%d)\n", ret); 1591eb77c3bSTalel Shenhar goto err_domain_remove; 1601eb77c3bSTalel Shenhar } 1611eb77c3bSTalel Shenhar 1621eb77c3bSTalel Shenhar gc = irq_get_domain_generic_chip(fic->domain, 0); 1631eb77c3bSTalel Shenhar gc->reg_base = fic->base; 1641eb77c3bSTalel Shenhar gc->chip_types->regs.mask = AL_FIC_MASK; 1651eb77c3bSTalel Shenhar gc->chip_types->regs.ack = AL_FIC_CAUSE; 1661eb77c3bSTalel Shenhar gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit; 1671eb77c3bSTalel Shenhar gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit; 1681eb77c3bSTalel Shenhar gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit; 1691eb77c3bSTalel Shenhar gc->chip_types->chip.irq_set_type = al_fic_irq_set_type; 1709c426b77STalel Shenhar gc->chip_types->chip.irq_retrigger = al_fic_irq_retrigger; 1711eb77c3bSTalel Shenhar gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE; 1721eb77c3bSTalel Shenhar gc->private = fic; 1731eb77c3bSTalel Shenhar 1741eb77c3bSTalel Shenhar irq_set_chained_handler_and_data(fic->parent_irq, 1751eb77c3bSTalel Shenhar al_fic_irq_handler, 1761eb77c3bSTalel Shenhar fic); 1771eb77c3bSTalel Shenhar return 0; 1781eb77c3bSTalel Shenhar 1791eb77c3bSTalel Shenhar err_domain_remove: 1801eb77c3bSTalel Shenhar irq_domain_remove(fic->domain); 1811eb77c3bSTalel Shenhar 1821eb77c3bSTalel Shenhar return ret; 1831eb77c3bSTalel Shenhar } 1841eb77c3bSTalel Shenhar 1851eb77c3bSTalel Shenhar /* 1861eb77c3bSTalel Shenhar * al_fic_wire_init() - initialize and configure fic in wire mode 1871eb77c3bSTalel Shenhar * @of_node: optional pointer to interrupt controller's device tree node. 1881eb77c3bSTalel Shenhar * @base: mmio to fic register 1891eb77c3bSTalel Shenhar * @name: name of the fic 1901eb77c3bSTalel Shenhar * @parent_irq: interrupt of parent 1911eb77c3bSTalel Shenhar * 1921eb77c3bSTalel Shenhar * This API will configure the fic hardware to to work in wire mode. 1931eb77c3bSTalel Shenhar * In wire mode, fic hardware is generating a wire ("wired") interrupt. 1941eb77c3bSTalel Shenhar * Interrupt can be generated based on positive edge or level - configuration is 1951eb77c3bSTalel Shenhar * to be determined based on connected hardware to this fic. 1961eb77c3bSTalel Shenhar */ 1971eb77c3bSTalel Shenhar static struct al_fic *al_fic_wire_init(struct device_node *node, 1981eb77c3bSTalel Shenhar void __iomem *base, 1991eb77c3bSTalel Shenhar const char *name, 2001eb77c3bSTalel Shenhar unsigned int parent_irq) 2011eb77c3bSTalel Shenhar { 2021eb77c3bSTalel Shenhar struct al_fic *fic; 2031eb77c3bSTalel Shenhar int ret; 2041eb77c3bSTalel Shenhar u32 control = CONTROL_MASK_MSI_X; 2051eb77c3bSTalel Shenhar 2061eb77c3bSTalel Shenhar fic = kzalloc(sizeof(*fic), GFP_KERNEL); 2071eb77c3bSTalel Shenhar if (!fic) 2081eb77c3bSTalel Shenhar return ERR_PTR(-ENOMEM); 2091eb77c3bSTalel Shenhar 2101eb77c3bSTalel Shenhar fic->base = base; 2111eb77c3bSTalel Shenhar fic->parent_irq = parent_irq; 2121eb77c3bSTalel Shenhar fic->name = name; 2131eb77c3bSTalel Shenhar 2141eb77c3bSTalel Shenhar /* mask out all interrupts */ 2151eb77c3bSTalel Shenhar writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK); 2161eb77c3bSTalel Shenhar 2171eb77c3bSTalel Shenhar /* clear any pending interrupt */ 2181eb77c3bSTalel Shenhar writel_relaxed(0, fic->base + AL_FIC_CAUSE); 2191eb77c3bSTalel Shenhar 2201eb77c3bSTalel Shenhar writel_relaxed(control, fic->base + AL_FIC_CONTROL); 2211eb77c3bSTalel Shenhar 2221eb77c3bSTalel Shenhar ret = al_fic_register(node, fic); 2231eb77c3bSTalel Shenhar if (ret) { 2241eb77c3bSTalel Shenhar pr_err("fail to register irqchip\n"); 2251eb77c3bSTalel Shenhar goto err_free; 2261eb77c3bSTalel Shenhar } 2271eb77c3bSTalel Shenhar 2281eb77c3bSTalel Shenhar pr_debug("%s initialized successfully in Legacy mode (parent-irq=%u)\n", 2291eb77c3bSTalel Shenhar fic->name, parent_irq); 2301eb77c3bSTalel Shenhar 2311eb77c3bSTalel Shenhar return fic; 2321eb77c3bSTalel Shenhar 2331eb77c3bSTalel Shenhar err_free: 2341eb77c3bSTalel Shenhar kfree(fic); 2351eb77c3bSTalel Shenhar return ERR_PTR(ret); 2361eb77c3bSTalel Shenhar } 2371eb77c3bSTalel Shenhar 2381eb77c3bSTalel Shenhar static int __init al_fic_init_dt(struct device_node *node, 2391eb77c3bSTalel Shenhar struct device_node *parent) 2401eb77c3bSTalel Shenhar { 2411eb77c3bSTalel Shenhar int ret; 2421eb77c3bSTalel Shenhar void __iomem *base; 2431eb77c3bSTalel Shenhar unsigned int parent_irq; 2441eb77c3bSTalel Shenhar struct al_fic *fic; 2451eb77c3bSTalel Shenhar 2461eb77c3bSTalel Shenhar if (!parent) { 2471eb77c3bSTalel Shenhar pr_err("%s: unsupported - device require a parent\n", 2481eb77c3bSTalel Shenhar node->name); 2491eb77c3bSTalel Shenhar return -EINVAL; 2501eb77c3bSTalel Shenhar } 2511eb77c3bSTalel Shenhar 2521eb77c3bSTalel Shenhar base = of_iomap(node, 0); 2531eb77c3bSTalel Shenhar if (!base) { 2541eb77c3bSTalel Shenhar pr_err("%s: fail to map memory\n", node->name); 2551eb77c3bSTalel Shenhar return -ENOMEM; 2561eb77c3bSTalel Shenhar } 2571eb77c3bSTalel Shenhar 2581eb77c3bSTalel Shenhar parent_irq = irq_of_parse_and_map(node, 0); 2591eb77c3bSTalel Shenhar if (!parent_irq) { 2601eb77c3bSTalel Shenhar pr_err("%s: fail to map irq\n", node->name); 2611eb77c3bSTalel Shenhar ret = -EINVAL; 2621eb77c3bSTalel Shenhar goto err_unmap; 2631eb77c3bSTalel Shenhar } 2641eb77c3bSTalel Shenhar 2651eb77c3bSTalel Shenhar fic = al_fic_wire_init(node, 2661eb77c3bSTalel Shenhar base, 2671eb77c3bSTalel Shenhar node->name, 2681eb77c3bSTalel Shenhar parent_irq); 2691eb77c3bSTalel Shenhar if (IS_ERR(fic)) { 2701eb77c3bSTalel Shenhar pr_err("%s: fail to initialize irqchip (%lu)\n", 2711eb77c3bSTalel Shenhar node->name, 2721eb77c3bSTalel Shenhar PTR_ERR(fic)); 2731eb77c3bSTalel Shenhar ret = PTR_ERR(fic); 2741eb77c3bSTalel Shenhar goto err_irq_dispose; 2751eb77c3bSTalel Shenhar } 2761eb77c3bSTalel Shenhar 2771eb77c3bSTalel Shenhar return 0; 2781eb77c3bSTalel Shenhar 2791eb77c3bSTalel Shenhar err_irq_dispose: 2801eb77c3bSTalel Shenhar irq_dispose_mapping(parent_irq); 2811eb77c3bSTalel Shenhar err_unmap: 2821eb77c3bSTalel Shenhar iounmap(base); 2831eb77c3bSTalel Shenhar 2841eb77c3bSTalel Shenhar return ret; 2851eb77c3bSTalel Shenhar } 2861eb77c3bSTalel Shenhar 2871eb77c3bSTalel Shenhar IRQCHIP_DECLARE(al_fic, "amazon,al-fic", al_fic_init_dt); 288