162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Texas Instruments' K3 Interrupt Aggregator irqchip driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/ 662306a36Sopenharmony_ci * Lokesh Vutla <lokeshvutla@ti.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/irq.h> 1262306a36Sopenharmony_ci#include <linux/irqchip.h> 1362306a36Sopenharmony_ci#include <linux/irqdomain.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/msi.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/moduleparam.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_irq.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 2262306a36Sopenharmony_ci#include <linux/soc/ti/ti_sci_inta_msi.h> 2362306a36Sopenharmony_ci#include <linux/soc/ti/ti_sci_protocol.h> 2462306a36Sopenharmony_ci#include <asm-generic/msi.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define TI_SCI_DEV_ID_MASK 0xffff 2762306a36Sopenharmony_ci#define TI_SCI_DEV_ID_SHIFT 16 2862306a36Sopenharmony_ci#define TI_SCI_IRQ_ID_MASK 0xffff 2962306a36Sopenharmony_ci#define TI_SCI_IRQ_ID_SHIFT 0 3062306a36Sopenharmony_ci#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ 3162306a36Sopenharmony_ci (TI_SCI_DEV_ID_MASK)) 3262306a36Sopenharmony_ci#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) 3362306a36Sopenharmony_ci#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ 3462306a36Sopenharmony_ci TI_SCI_DEV_ID_SHIFT) | \ 3562306a36Sopenharmony_ci ((index) & TI_SCI_IRQ_ID_MASK)) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MAX_EVENTS_PER_VINT 64 3862306a36Sopenharmony_ci#define VINT_ENABLE_SET_OFFSET 0x0 3962306a36Sopenharmony_ci#define VINT_ENABLE_CLR_OFFSET 0x8 4062306a36Sopenharmony_ci#define VINT_STATUS_OFFSET 0x18 4162306a36Sopenharmony_ci#define VINT_STATUS_MASKED_OFFSET 0x20 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * struct ti_sci_inta_event_desc - Description of an event coming to 4562306a36Sopenharmony_ci * Interrupt Aggregator. This serves 4662306a36Sopenharmony_ci * as a mapping table for global event, 4762306a36Sopenharmony_ci * hwirq and vint bit. 4862306a36Sopenharmony_ci * @global_event: Global event number corresponding to this event 4962306a36Sopenharmony_ci * @hwirq: Hwirq of the incoming interrupt 5062306a36Sopenharmony_ci * @vint_bit: Corresponding vint bit to which this event is attached. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistruct ti_sci_inta_event_desc { 5362306a36Sopenharmony_ci u16 global_event; 5462306a36Sopenharmony_ci u32 hwirq; 5562306a36Sopenharmony_ci u8 vint_bit; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out 6062306a36Sopenharmony_ci * of Interrupt Aggregator. 6162306a36Sopenharmony_ci * @domain: Pointer to IRQ domain to which this vint belongs. 6262306a36Sopenharmony_ci * @list: List entry for the vint list 6362306a36Sopenharmony_ci * @event_map: Bitmap to manage the allocation of events to vint. 6462306a36Sopenharmony_ci * @events: Array of event descriptors assigned to this vint. 6562306a36Sopenharmony_ci * @parent_virq: Linux IRQ number that gets attached to parent 6662306a36Sopenharmony_ci * @vint_id: TISCI vint ID 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistruct ti_sci_inta_vint_desc { 6962306a36Sopenharmony_ci struct irq_domain *domain; 7062306a36Sopenharmony_ci struct list_head list; 7162306a36Sopenharmony_ci DECLARE_BITMAP(event_map, MAX_EVENTS_PER_VINT); 7262306a36Sopenharmony_ci struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT]; 7362306a36Sopenharmony_ci unsigned int parent_virq; 7462306a36Sopenharmony_ci u16 vint_id; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/** 7862306a36Sopenharmony_ci * struct ti_sci_inta_irq_domain - Structure representing a TISCI based 7962306a36Sopenharmony_ci * Interrupt Aggregator IRQ domain. 8062306a36Sopenharmony_ci * @sci: Pointer to TISCI handle 8162306a36Sopenharmony_ci * @vint: TISCI resource pointer representing IA interrupts. 8262306a36Sopenharmony_ci * @global_event: TISCI resource pointer representing global events. 8362306a36Sopenharmony_ci * @vint_list: List of the vints active in the system 8462306a36Sopenharmony_ci * @vint_mutex: Mutex to protect vint_list 8562306a36Sopenharmony_ci * @base: Base address of the memory mapped IO registers 8662306a36Sopenharmony_ci * @pdev: Pointer to platform device. 8762306a36Sopenharmony_ci * @ti_sci_id: TI-SCI device identifier 8862306a36Sopenharmony_ci * @unmapped_cnt: Number of @unmapped_dev_ids entries 8962306a36Sopenharmony_ci * @unmapped_dev_ids: Pointer to an array of TI-SCI device identifiers of 9062306a36Sopenharmony_ci * unmapped event sources. 9162306a36Sopenharmony_ci * Unmapped Events are not part of the Global Event Map and 9262306a36Sopenharmony_ci * they are converted to Global event within INTA to be 9362306a36Sopenharmony_ci * received by the same INTA to generate an interrupt. 9462306a36Sopenharmony_ci * In case an interrupt request comes for a device which is 9562306a36Sopenharmony_ci * generating Unmapped Event, we must use the INTA's TI-SCI 9662306a36Sopenharmony_ci * device identifier in place of the source device 9762306a36Sopenharmony_ci * identifier to let sysfw know where it has to program the 9862306a36Sopenharmony_ci * Global Event number. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistruct ti_sci_inta_irq_domain { 10162306a36Sopenharmony_ci const struct ti_sci_handle *sci; 10262306a36Sopenharmony_ci struct ti_sci_resource *vint; 10362306a36Sopenharmony_ci struct ti_sci_resource *global_event; 10462306a36Sopenharmony_ci struct list_head vint_list; 10562306a36Sopenharmony_ci /* Mutex to protect vint list */ 10662306a36Sopenharmony_ci struct mutex vint_mutex; 10762306a36Sopenharmony_ci void __iomem *base; 10862306a36Sopenharmony_ci struct platform_device *pdev; 10962306a36Sopenharmony_ci u32 ti_sci_id; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci int unmapped_cnt; 11262306a36Sopenharmony_ci u16 *unmapped_dev_ids; 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \ 11662306a36Sopenharmony_ci events[i]) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic u16 ti_sci_inta_get_dev_id(struct ti_sci_inta_irq_domain *inta, u32 hwirq) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u16 dev_id = HWIRQ_TO_DEVID(hwirq); 12162306a36Sopenharmony_ci int i; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (inta->unmapped_cnt == 0) 12462306a36Sopenharmony_ci return dev_id; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * For devices sending Unmapped Events we must use the INTA's TI-SCI 12862306a36Sopenharmony_ci * device identifier number to be able to convert it to a Global Event 12962306a36Sopenharmony_ci * and map it to an interrupt. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci for (i = 0; i < inta->unmapped_cnt; i++) { 13262306a36Sopenharmony_ci if (dev_id == inta->unmapped_dev_ids[i]) { 13362306a36Sopenharmony_ci dev_id = inta->ti_sci_id; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return dev_id; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/** 14262306a36Sopenharmony_ci * ti_sci_inta_irq_handler() - Chained IRQ handler for the vint irqs 14362306a36Sopenharmony_ci * @desc: Pointer to irq_desc corresponding to the irq 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic void ti_sci_inta_irq_handler(struct irq_desc *desc) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct ti_sci_inta_vint_desc *vint_desc; 14862306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta; 14962306a36Sopenharmony_ci struct irq_domain *domain; 15062306a36Sopenharmony_ci unsigned int bit; 15162306a36Sopenharmony_ci unsigned long val; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci vint_desc = irq_desc_get_handler_data(desc); 15462306a36Sopenharmony_ci domain = vint_desc->domain; 15562306a36Sopenharmony_ci inta = domain->host_data; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci chained_irq_enter(irq_desc_get_chip(desc), desc); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 + 16062306a36Sopenharmony_ci VINT_STATUS_MASKED_OFFSET); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) 16362306a36Sopenharmony_ci generic_handle_domain_irq(domain, vint_desc->events[bit].hwirq); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci chained_irq_exit(irq_desc_get_chip(desc), desc); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * ti_sci_inta_xlate_irq() - Translate hwirq to parent's hwirq. 17062306a36Sopenharmony_ci * @inta: IRQ domain corresponding to Interrupt Aggregator 17162306a36Sopenharmony_ci * @vint_id: Hardware irq corresponding to the above irq domain 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * Return parent irq number if translation is available else -ENOENT. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic int ti_sci_inta_xlate_irq(struct ti_sci_inta_irq_domain *inta, 17662306a36Sopenharmony_ci u16 vint_id) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct device_node *np = dev_of_node(&inta->pdev->dev); 17962306a36Sopenharmony_ci u32 base, parent_base, size; 18062306a36Sopenharmony_ci const __be32 *range; 18162306a36Sopenharmony_ci int len; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci range = of_get_property(np, "ti,interrupt-ranges", &len); 18462306a36Sopenharmony_ci if (!range) 18562306a36Sopenharmony_ci return vint_id; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (len /= sizeof(*range); len >= 3; len -= 3) { 18862306a36Sopenharmony_ci base = be32_to_cpu(*range++); 18962306a36Sopenharmony_ci parent_base = be32_to_cpu(*range++); 19062306a36Sopenharmony_ci size = be32_to_cpu(*range++); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (base <= vint_id && vint_id < base + size) 19362306a36Sopenharmony_ci return vint_id - base + parent_base; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return -ENOENT; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/** 20062306a36Sopenharmony_ci * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator 20162306a36Sopenharmony_ci * @domain: IRQ domain corresponding to Interrupt Aggregator 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Return 0 if all went well else corresponding error value. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistatic struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta = domain->host_data; 20862306a36Sopenharmony_ci struct ti_sci_inta_vint_desc *vint_desc; 20962306a36Sopenharmony_ci struct irq_fwspec parent_fwspec; 21062306a36Sopenharmony_ci struct device_node *parent_node; 21162306a36Sopenharmony_ci unsigned int parent_virq; 21262306a36Sopenharmony_ci int p_hwirq, ret; 21362306a36Sopenharmony_ci u16 vint_id; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci vint_id = ti_sci_get_free_resource(inta->vint); 21662306a36Sopenharmony_ci if (vint_id == TI_SCI_RESOURCE_NULL) 21762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci p_hwirq = ti_sci_inta_xlate_irq(inta, vint_id); 22062306a36Sopenharmony_ci if (p_hwirq < 0) { 22162306a36Sopenharmony_ci ret = p_hwirq; 22262306a36Sopenharmony_ci goto free_vint; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); 22662306a36Sopenharmony_ci if (!vint_desc) { 22762306a36Sopenharmony_ci ret = -ENOMEM; 22862306a36Sopenharmony_ci goto free_vint; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci vint_desc->domain = domain; 23262306a36Sopenharmony_ci vint_desc->vint_id = vint_id; 23362306a36Sopenharmony_ci INIT_LIST_HEAD(&vint_desc->list); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci parent_node = of_irq_find_parent(dev_of_node(&inta->pdev->dev)); 23662306a36Sopenharmony_ci parent_fwspec.fwnode = of_node_to_fwnode(parent_node); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (of_device_is_compatible(parent_node, "arm,gic-v3")) { 23962306a36Sopenharmony_ci /* Parent is GIC */ 24062306a36Sopenharmony_ci parent_fwspec.param_count = 3; 24162306a36Sopenharmony_ci parent_fwspec.param[0] = 0; 24262306a36Sopenharmony_ci parent_fwspec.param[1] = p_hwirq - 32; 24362306a36Sopenharmony_ci parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci /* Parent is Interrupt Router */ 24662306a36Sopenharmony_ci parent_fwspec.param_count = 1; 24762306a36Sopenharmony_ci parent_fwspec.param[0] = p_hwirq; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci parent_virq = irq_create_fwspec_mapping(&parent_fwspec); 25162306a36Sopenharmony_ci if (parent_virq == 0) { 25262306a36Sopenharmony_ci dev_err(&inta->pdev->dev, "Parent IRQ allocation failed\n"); 25362306a36Sopenharmony_ci ret = -EINVAL; 25462306a36Sopenharmony_ci goto free_vint_desc; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci vint_desc->parent_virq = parent_virq; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci list_add_tail(&vint_desc->list, &inta->vint_list); 26062306a36Sopenharmony_ci irq_set_chained_handler_and_data(vint_desc->parent_virq, 26162306a36Sopenharmony_ci ti_sci_inta_irq_handler, vint_desc); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return vint_desc; 26462306a36Sopenharmony_cifree_vint_desc: 26562306a36Sopenharmony_ci kfree(vint_desc); 26662306a36Sopenharmony_cifree_vint: 26762306a36Sopenharmony_ci ti_sci_release_resource(inta->vint, vint_id); 26862306a36Sopenharmony_ci return ERR_PTR(ret); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * ti_sci_inta_alloc_event() - Attach an event to a IA vint. 27362306a36Sopenharmony_ci * @vint_desc: Pointer to vint_desc to which the event gets attached 27462306a36Sopenharmony_ci * @free_bit: Bit inside vint to which event gets attached 27562306a36Sopenharmony_ci * @hwirq: hwirq of the input event 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * Return event_desc pointer if all went ok else appropriate error value. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic struct ti_sci_inta_event_desc *ti_sci_inta_alloc_event(struct ti_sci_inta_vint_desc *vint_desc, 28062306a36Sopenharmony_ci u16 free_bit, 28162306a36Sopenharmony_ci u32 hwirq) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta = vint_desc->domain->host_data; 28462306a36Sopenharmony_ci struct ti_sci_inta_event_desc *event_desc; 28562306a36Sopenharmony_ci u16 dev_id, dev_index; 28662306a36Sopenharmony_ci int err; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci dev_id = ti_sci_inta_get_dev_id(inta, hwirq); 28962306a36Sopenharmony_ci dev_index = HWIRQ_TO_IRQID(hwirq); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci event_desc = &vint_desc->events[free_bit]; 29262306a36Sopenharmony_ci event_desc->hwirq = hwirq; 29362306a36Sopenharmony_ci event_desc->vint_bit = free_bit; 29462306a36Sopenharmony_ci event_desc->global_event = ti_sci_get_free_resource(inta->global_event); 29562306a36Sopenharmony_ci if (event_desc->global_event == TI_SCI_RESOURCE_NULL) 29662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci err = inta->sci->ops.rm_irq_ops.set_event_map(inta->sci, 29962306a36Sopenharmony_ci dev_id, dev_index, 30062306a36Sopenharmony_ci inta->ti_sci_id, 30162306a36Sopenharmony_ci vint_desc->vint_id, 30262306a36Sopenharmony_ci event_desc->global_event, 30362306a36Sopenharmony_ci free_bit); 30462306a36Sopenharmony_ci if (err) 30562306a36Sopenharmony_ci goto free_global_event; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return event_desc; 30862306a36Sopenharmony_cifree_global_event: 30962306a36Sopenharmony_ci ti_sci_release_resource(inta->global_event, event_desc->global_event); 31062306a36Sopenharmony_ci return ERR_PTR(err); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/** 31462306a36Sopenharmony_ci * ti_sci_inta_alloc_irq() - Allocate an irq within INTA domain 31562306a36Sopenharmony_ci * @domain: irq_domain pointer corresponding to INTA 31662306a36Sopenharmony_ci * @hwirq: hwirq of the input event 31762306a36Sopenharmony_ci * 31862306a36Sopenharmony_ci * Note: Allocation happens in the following manner: 31962306a36Sopenharmony_ci * - Find a free bit available in any of the vints available in the list. 32062306a36Sopenharmony_ci * - If not found, allocate a vint from the vint pool 32162306a36Sopenharmony_ci * - Attach the free bit to input hwirq. 32262306a36Sopenharmony_ci * Return event_desc if all went ok else appropriate error value. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_cistatic struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *domain, 32562306a36Sopenharmony_ci u32 hwirq) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta = domain->host_data; 32862306a36Sopenharmony_ci struct ti_sci_inta_vint_desc *vint_desc = NULL; 32962306a36Sopenharmony_ci struct ti_sci_inta_event_desc *event_desc; 33062306a36Sopenharmony_ci u16 free_bit; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci mutex_lock(&inta->vint_mutex); 33362306a36Sopenharmony_ci list_for_each_entry(vint_desc, &inta->vint_list, list) { 33462306a36Sopenharmony_ci free_bit = find_first_zero_bit(vint_desc->event_map, 33562306a36Sopenharmony_ci MAX_EVENTS_PER_VINT); 33662306a36Sopenharmony_ci if (free_bit != MAX_EVENTS_PER_VINT) { 33762306a36Sopenharmony_ci set_bit(free_bit, vint_desc->event_map); 33862306a36Sopenharmony_ci goto alloc_event; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* No free bits available. Allocate a new vint */ 34362306a36Sopenharmony_ci vint_desc = ti_sci_inta_alloc_parent_irq(domain); 34462306a36Sopenharmony_ci if (IS_ERR(vint_desc)) { 34562306a36Sopenharmony_ci event_desc = ERR_CAST(vint_desc); 34662306a36Sopenharmony_ci goto unlock; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci free_bit = find_first_zero_bit(vint_desc->event_map, 35062306a36Sopenharmony_ci MAX_EVENTS_PER_VINT); 35162306a36Sopenharmony_ci set_bit(free_bit, vint_desc->event_map); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cialloc_event: 35462306a36Sopenharmony_ci event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq); 35562306a36Sopenharmony_ci if (IS_ERR(event_desc)) 35662306a36Sopenharmony_ci clear_bit(free_bit, vint_desc->event_map); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciunlock: 35962306a36Sopenharmony_ci mutex_unlock(&inta->vint_mutex); 36062306a36Sopenharmony_ci return event_desc; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/** 36462306a36Sopenharmony_ci * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA 36562306a36Sopenharmony_ci * @inta: Pointer to inta domain. 36662306a36Sopenharmony_ci * @vint_desc: Pointer to vint_desc that needs to be freed. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta, 36962306a36Sopenharmony_ci struct ti_sci_inta_vint_desc *vint_desc) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) { 37262306a36Sopenharmony_ci list_del(&vint_desc->list); 37362306a36Sopenharmony_ci ti_sci_release_resource(inta->vint, vint_desc->vint_id); 37462306a36Sopenharmony_ci irq_dispose_mapping(vint_desc->parent_virq); 37562306a36Sopenharmony_ci kfree(vint_desc); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * ti_sci_inta_free_irq() - Free an IRQ within INTA domain 38162306a36Sopenharmony_ci * @event_desc: Pointer to event_desc that needs to be freed. 38262306a36Sopenharmony_ci * @hwirq: Hwirq number within INTA domain that needs to be freed 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_cistatic void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc, 38562306a36Sopenharmony_ci u32 hwirq) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct ti_sci_inta_vint_desc *vint_desc; 38862306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta; 38962306a36Sopenharmony_ci u16 dev_id; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); 39262306a36Sopenharmony_ci inta = vint_desc->domain->host_data; 39362306a36Sopenharmony_ci dev_id = ti_sci_inta_get_dev_id(inta, hwirq); 39462306a36Sopenharmony_ci /* free event irq */ 39562306a36Sopenharmony_ci mutex_lock(&inta->vint_mutex); 39662306a36Sopenharmony_ci inta->sci->ops.rm_irq_ops.free_event_map(inta->sci, 39762306a36Sopenharmony_ci dev_id, HWIRQ_TO_IRQID(hwirq), 39862306a36Sopenharmony_ci inta->ti_sci_id, 39962306a36Sopenharmony_ci vint_desc->vint_id, 40062306a36Sopenharmony_ci event_desc->global_event, 40162306a36Sopenharmony_ci event_desc->vint_bit); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci clear_bit(event_desc->vint_bit, vint_desc->event_map); 40462306a36Sopenharmony_ci ti_sci_release_resource(inta->global_event, event_desc->global_event); 40562306a36Sopenharmony_ci event_desc->global_event = TI_SCI_RESOURCE_NULL; 40662306a36Sopenharmony_ci event_desc->hwirq = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ti_sci_inta_free_parent_irq(inta, vint_desc); 40962306a36Sopenharmony_ci mutex_unlock(&inta->vint_mutex); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/** 41362306a36Sopenharmony_ci * ti_sci_inta_request_resources() - Allocate resources for input irq 41462306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 41562306a36Sopenharmony_ci * 41662306a36Sopenharmony_ci * Note: This is the core api where the actual allocation happens for input 41762306a36Sopenharmony_ci * hwirq. This allocation involves creating a parent irq for vint. 41862306a36Sopenharmony_ci * If this is done in irq_domain_ops.alloc() then a deadlock is reached 41962306a36Sopenharmony_ci * for allocation. So this allocation is being done in request_resources() 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * Return: 0 if all went well else corresponding error. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_cistatic int ti_sci_inta_request_resources(struct irq_data *data) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct ti_sci_inta_event_desc *event_desc; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq); 42862306a36Sopenharmony_ci if (IS_ERR(event_desc)) 42962306a36Sopenharmony_ci return PTR_ERR(event_desc); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci data->chip_data = event_desc; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return 0; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * ti_sci_inta_release_resources - Release resources for input irq 43862306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Note: Corresponding to request_resources(), all the unmapping and deletion 44162306a36Sopenharmony_ci * of parent vint irqs happens in this api. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic void ti_sci_inta_release_resources(struct irq_data *data) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct ti_sci_inta_event_desc *event_desc; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci event_desc = irq_data_get_irq_chip_data(data); 44862306a36Sopenharmony_ci ti_sci_inta_free_irq(event_desc, data->hwirq); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/** 45262306a36Sopenharmony_ci * ti_sci_inta_manage_event() - Control the event based on the offset 45362306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 45462306a36Sopenharmony_ci * @offset: register offset using which event is controlled. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic void ti_sci_inta_manage_event(struct irq_data *data, u32 offset) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct ti_sci_inta_event_desc *event_desc; 45962306a36Sopenharmony_ci struct ti_sci_inta_vint_desc *vint_desc; 46062306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci event_desc = irq_data_get_irq_chip_data(data); 46362306a36Sopenharmony_ci vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); 46462306a36Sopenharmony_ci inta = data->domain->host_data; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci writeq_relaxed(BIT(event_desc->vint_bit), 46762306a36Sopenharmony_ci inta->base + vint_desc->vint_id * 0x1000 + offset); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * ti_sci_inta_mask_irq() - Mask an event 47262306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cistatic void ti_sci_inta_mask_irq(struct irq_data *data) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * ti_sci_inta_unmask_irq() - Unmask an event 48162306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic void ti_sci_inta_unmask_irq(struct irq_data *data) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/** 48962306a36Sopenharmony_ci * ti_sci_inta_ack_irq() - Ack an event 49062306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_cistatic void ti_sci_inta_ack_irq(struct irq_data *data) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * Do not clear the event if hardware is capable of sending 49662306a36Sopenharmony_ci * a down event. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH) 49962306a36Sopenharmony_ci ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int ti_sci_inta_set_affinity(struct irq_data *d, 50362306a36Sopenharmony_ci const struct cpumask *mask_val, bool force) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/** 50962306a36Sopenharmony_ci * ti_sci_inta_set_type() - Update the trigger type of the irq. 51062306a36Sopenharmony_ci * @data: Pointer to corresponding irq_data 51162306a36Sopenharmony_ci * @type: Trigger type as specified by user 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * Note: This updates the handle_irq callback for level msi. 51462306a36Sopenharmony_ci * 51562306a36Sopenharmony_ci * Return 0 if all went well else appropriate error. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_cistatic int ti_sci_inta_set_type(struct irq_data *data, unsigned int type) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci /* 52062306a36Sopenharmony_ci * .alloc default sets handle_edge_irq. But if the user specifies 52162306a36Sopenharmony_ci * that IRQ is level MSI, then update the handle to handle_level_irq 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 52462306a36Sopenharmony_ci case IRQF_TRIGGER_HIGH: 52562306a36Sopenharmony_ci irq_set_handler_locked(data, handle_level_irq); 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci case IRQF_TRIGGER_RISING: 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci default: 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic struct irq_chip ti_sci_inta_irq_chip = { 53562306a36Sopenharmony_ci .name = "INTA", 53662306a36Sopenharmony_ci .irq_ack = ti_sci_inta_ack_irq, 53762306a36Sopenharmony_ci .irq_mask = ti_sci_inta_mask_irq, 53862306a36Sopenharmony_ci .irq_set_type = ti_sci_inta_set_type, 53962306a36Sopenharmony_ci .irq_unmask = ti_sci_inta_unmask_irq, 54062306a36Sopenharmony_ci .irq_set_affinity = ti_sci_inta_set_affinity, 54162306a36Sopenharmony_ci .irq_request_resources = ti_sci_inta_request_resources, 54262306a36Sopenharmony_ci .irq_release_resources = ti_sci_inta_release_resources, 54362306a36Sopenharmony_ci}; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/** 54662306a36Sopenharmony_ci * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain 54762306a36Sopenharmony_ci * @domain: Domain to which the irqs belong 54862306a36Sopenharmony_ci * @virq: base linux virtual IRQ to be freed. 54962306a36Sopenharmony_ci * @nr_irqs: Number of continuous irqs to be freed 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_cistatic void ti_sci_inta_irq_domain_free(struct irq_domain *domain, 55262306a36Sopenharmony_ci unsigned int virq, unsigned int nr_irqs) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct irq_data *data = irq_domain_get_irq_data(domain, virq); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci irq_domain_reset_irq_data(data); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci/** 56062306a36Sopenharmony_ci * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs 56162306a36Sopenharmony_ci * @domain: Point to the interrupt aggregator IRQ domain 56262306a36Sopenharmony_ci * @virq: Corresponding Linux virtual IRQ number 56362306a36Sopenharmony_ci * @nr_irqs: Continuous irqs to be allocated 56462306a36Sopenharmony_ci * @data: Pointer to firmware specifier 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci * No actual allocation happens here. 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * Return 0 if all went well else appropriate error value. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_cistatic int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain, 57162306a36Sopenharmony_ci unsigned int virq, unsigned int nr_irqs, 57262306a36Sopenharmony_ci void *data) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci msi_alloc_info_t *arg = data; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip, 57762306a36Sopenharmony_ci NULL, handle_edge_irq, NULL, NULL); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { 58362306a36Sopenharmony_ci .free = ti_sci_inta_irq_domain_free, 58462306a36Sopenharmony_ci .alloc = ti_sci_inta_irq_domain_alloc, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic struct irq_chip ti_sci_inta_msi_irq_chip = { 58862306a36Sopenharmony_ci .name = "MSI-INTA", 58962306a36Sopenharmony_ci .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, 59062306a36Sopenharmony_ci}; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg, 59362306a36Sopenharmony_ci struct msi_desc *desc) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(desc->dev); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci arg->desc = desc; 59862306a36Sopenharmony_ci arg->hwirq = TO_HWIRQ(pdev->id, desc->msi_index); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic struct msi_domain_ops ti_sci_inta_msi_ops = { 60262306a36Sopenharmony_ci .set_desc = ti_sci_inta_msi_set_desc, 60362306a36Sopenharmony_ci}; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic struct msi_domain_info ti_sci_inta_msi_domain_info = { 60662306a36Sopenharmony_ci .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 60762306a36Sopenharmony_ci MSI_FLAG_LEVEL_CAPABLE), 60862306a36Sopenharmony_ci .ops = &ti_sci_inta_msi_ops, 60962306a36Sopenharmony_ci .chip = &ti_sci_inta_msi_irq_chip, 61062306a36Sopenharmony_ci}; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int ti_sci_inta_get_unmapped_sources(struct ti_sci_inta_irq_domain *inta) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct device *dev = &inta->pdev->dev; 61562306a36Sopenharmony_ci struct device_node *node = dev_of_node(dev); 61662306a36Sopenharmony_ci struct of_phandle_iterator it; 61762306a36Sopenharmony_ci int count, err, ret, i; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci count = of_count_phandle_with_args(node, "ti,unmapped-event-sources", NULL); 62062306a36Sopenharmony_ci if (count <= 0) 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci inta->unmapped_dev_ids = devm_kcalloc(dev, count, 62462306a36Sopenharmony_ci sizeof(*inta->unmapped_dev_ids), 62562306a36Sopenharmony_ci GFP_KERNEL); 62662306a36Sopenharmony_ci if (!inta->unmapped_dev_ids) 62762306a36Sopenharmony_ci return -ENOMEM; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci i = 0; 63062306a36Sopenharmony_ci of_for_each_phandle(&it, err, node, "ti,unmapped-event-sources", NULL, 0) { 63162306a36Sopenharmony_ci u32 dev_id; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ret = of_property_read_u32(it.node, "ti,sci-dev-id", &dev_id); 63462306a36Sopenharmony_ci if (ret) { 63562306a36Sopenharmony_ci dev_err(dev, "ti,sci-dev-id read failure for %pOFf\n", it.node); 63662306a36Sopenharmony_ci of_node_put(it.node); 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci inta->unmapped_dev_ids[i++] = dev_id; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci inta->unmapped_cnt = count; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct irq_domain *parent_domain, *domain, *msi_domain; 65062306a36Sopenharmony_ci struct device_node *parent_node, *node; 65162306a36Sopenharmony_ci struct ti_sci_inta_irq_domain *inta; 65262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 65362306a36Sopenharmony_ci int ret; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci node = dev_of_node(dev); 65662306a36Sopenharmony_ci parent_node = of_irq_find_parent(node); 65762306a36Sopenharmony_ci if (!parent_node) { 65862306a36Sopenharmony_ci dev_err(dev, "Failed to get IRQ parent node\n"); 65962306a36Sopenharmony_ci return -ENODEV; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci parent_domain = irq_find_host(parent_node); 66362306a36Sopenharmony_ci if (!parent_domain) 66462306a36Sopenharmony_ci return -EPROBE_DEFER; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL); 66762306a36Sopenharmony_ci if (!inta) 66862306a36Sopenharmony_ci return -ENOMEM; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci inta->pdev = pdev; 67162306a36Sopenharmony_ci inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); 67262306a36Sopenharmony_ci if (IS_ERR(inta->sci)) 67362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(inta->sci), 67462306a36Sopenharmony_ci "ti,sci read fail\n"); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &inta->ti_sci_id); 67762306a36Sopenharmony_ci if (ret) { 67862306a36Sopenharmony_ci dev_err(dev, "missing 'ti,sci-dev-id' property\n"); 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci inta->vint = devm_ti_sci_get_resource(inta->sci, dev, inta->ti_sci_id, 68362306a36Sopenharmony_ci TI_SCI_RESASG_SUBTYPE_IA_VINT); 68462306a36Sopenharmony_ci if (IS_ERR(inta->vint)) { 68562306a36Sopenharmony_ci dev_err(dev, "VINT resource allocation failed\n"); 68662306a36Sopenharmony_ci return PTR_ERR(inta->vint); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci inta->global_event = devm_ti_sci_get_resource(inta->sci, dev, inta->ti_sci_id, 69062306a36Sopenharmony_ci TI_SCI_RESASG_SUBTYPE_GLOBAL_EVENT_SEVT); 69162306a36Sopenharmony_ci if (IS_ERR(inta->global_event)) { 69262306a36Sopenharmony_ci dev_err(dev, "Global event resource allocation failed\n"); 69362306a36Sopenharmony_ci return PTR_ERR(inta->global_event); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci inta->base = devm_platform_ioremap_resource(pdev, 0); 69762306a36Sopenharmony_ci if (IS_ERR(inta->base)) 69862306a36Sopenharmony_ci return PTR_ERR(inta->base); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci ret = ti_sci_inta_get_unmapped_sources(inta); 70162306a36Sopenharmony_ci if (ret) 70262306a36Sopenharmony_ci return ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci domain = irq_domain_add_linear(dev_of_node(dev), 70562306a36Sopenharmony_ci ti_sci_get_num_resources(inta->vint), 70662306a36Sopenharmony_ci &ti_sci_inta_irq_domain_ops, inta); 70762306a36Sopenharmony_ci if (!domain) { 70862306a36Sopenharmony_ci dev_err(dev, "Failed to allocate IRQ domain\n"); 70962306a36Sopenharmony_ci return -ENOMEM; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), 71362306a36Sopenharmony_ci &ti_sci_inta_msi_domain_info, 71462306a36Sopenharmony_ci domain); 71562306a36Sopenharmony_ci if (!msi_domain) { 71662306a36Sopenharmony_ci irq_domain_remove(domain); 71762306a36Sopenharmony_ci dev_err(dev, "Failed to allocate msi domain\n"); 71862306a36Sopenharmony_ci return -ENOMEM; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci INIT_LIST_HEAD(&inta->vint_list); 72262306a36Sopenharmony_ci mutex_init(&inta->vint_mutex); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci dev_info(dev, "Interrupt Aggregator domain %d created\n", inta->ti_sci_id); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic const struct of_device_id ti_sci_inta_irq_domain_of_match[] = { 73062306a36Sopenharmony_ci { .compatible = "ti,sci-inta", }, 73162306a36Sopenharmony_ci { /* sentinel */ }, 73262306a36Sopenharmony_ci}; 73362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic struct platform_driver ti_sci_inta_irq_domain_driver = { 73662306a36Sopenharmony_ci .probe = ti_sci_inta_irq_domain_probe, 73762306a36Sopenharmony_ci .driver = { 73862306a36Sopenharmony_ci .name = "ti-sci-inta", 73962306a36Sopenharmony_ci .of_match_table = ti_sci_inta_irq_domain_of_match, 74062306a36Sopenharmony_ci }, 74162306a36Sopenharmony_ci}; 74262306a36Sopenharmony_cimodule_platform_driver(ti_sci_inta_irq_domain_driver); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ciMODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ti.com>"); 74562306a36Sopenharmony_ciMODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); 746