1/* 2 * ACPI GSI IRQ layer 3 * 4 * Copyright (C) 2015 ARM Ltd. 5 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11#include <linux/acpi.h> 12#include <linux/irq.h> 13#include <linux/irqdomain.h> 14#include <linux/of.h> 15 16enum acpi_irq_model_id acpi_irq_model; 17 18static struct fwnode_handle *acpi_gsi_domain_id; 19 20static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) 21{ 22 switch (polarity) { 23 case ACPI_ACTIVE_LOW: 24 return trigger == ACPI_EDGE_SENSITIVE ? 25 IRQ_TYPE_EDGE_FALLING : 26 IRQ_TYPE_LEVEL_LOW; 27 case ACPI_ACTIVE_HIGH: 28 return trigger == ACPI_EDGE_SENSITIVE ? 29 IRQ_TYPE_EDGE_RISING : 30 IRQ_TYPE_LEVEL_HIGH; 31 case ACPI_ACTIVE_BOTH: 32 if (trigger == ACPI_EDGE_SENSITIVE) 33 return IRQ_TYPE_EDGE_BOTH; 34 default: 35 return IRQ_TYPE_NONE; 36 } 37} 38 39/** 40 * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI 41 * @gsi: GSI IRQ number to map 42 * @irq: pointer where linux IRQ number is stored 43 * 44 * irq location updated with irq value [>0 on success, 0 on failure] 45 * 46 * Returns: linux IRQ number on success (>0) 47 * -EINVAL on failure 48 */ 49int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) 50{ 51 struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, 52 DOMAIN_BUS_ANY); 53 54 *irq = irq_find_mapping(d, gsi); 55 /* 56 * *irq == 0 means no mapping, that should 57 * be reported as a failure 58 */ 59 return (*irq > 0) ? *irq : -EINVAL; 60} 61EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); 62 63/** 64 * acpi_register_gsi() - Map a GSI to a linux IRQ number 65 * @dev: device for which IRQ has to be mapped 66 * @gsi: GSI IRQ number 67 * @trigger: trigger type of the GSI number to be mapped 68 * @polarity: polarity of the GSI to be mapped 69 * 70 * Returns: a valid linux IRQ number on success 71 * -EINVAL on failure 72 */ 73int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, 74 int polarity) 75{ 76 struct irq_fwspec fwspec; 77 78 if (WARN_ON(!acpi_gsi_domain_id)) { 79 pr_warn("GSI: No registered irqchip, giving up\n"); 80 return -EINVAL; 81 } 82 83 fwspec.fwnode = acpi_gsi_domain_id; 84 fwspec.param[0] = gsi; 85 fwspec.param[1] = acpi_gsi_get_irq_type(trigger, polarity); 86 fwspec.param_count = 2; 87 88 return irq_create_fwspec_mapping(&fwspec); 89} 90EXPORT_SYMBOL_GPL(acpi_register_gsi); 91 92/** 93 * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping 94 * @gsi: GSI IRQ number 95 */ 96void acpi_unregister_gsi(u32 gsi) 97{ 98 struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, 99 DOMAIN_BUS_ANY); 100 int irq = irq_find_mapping(d, gsi); 101 102 irq_dispose_mapping(irq); 103} 104EXPORT_SYMBOL_GPL(acpi_unregister_gsi); 105 106/** 107 * acpi_set_irq_model - Setup the GSI irqdomain information 108 * @model: the value assigned to acpi_irq_model 109 * @fwnode: the irq_domain identifier for mapping and looking up 110 * GSI interrupts 111 */ 112void __init acpi_set_irq_model(enum acpi_irq_model_id model, 113 struct fwnode_handle *fwnode) 114{ 115 acpi_irq_model = model; 116 acpi_gsi_domain_id = fwnode; 117} 118