162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Derived from arch/i386/kernel/irq.c 462306a36Sopenharmony_ci * Copyright (C) 1992 Linus Torvalds 562306a36Sopenharmony_ci * Adapted from arch/i386 by Gary Thomas 662306a36Sopenharmony_ci * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 762306a36Sopenharmony_ci * Updated and modified by Cort Dougan <cort@fsmlabs.com> 862306a36Sopenharmony_ci * Copyright (C) 1996-2001 Cort Dougan 962306a36Sopenharmony_ci * Adapted for Power Macintosh by Paul Mackerras 1062306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file contains the code used to make IRQ descriptions in the 1362306a36Sopenharmony_ci * device tree to actual irq numbers on an interrupt controller 1462306a36Sopenharmony_ci * driver. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) "OF: " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/list.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_irq.h> 2562306a36Sopenharmony_ci#include <linux/string.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/** 2962306a36Sopenharmony_ci * irq_of_parse_and_map - Parse and map an interrupt into linux virq space 3062306a36Sopenharmony_ci * @dev: Device node of the device whose interrupt is to be mapped 3162306a36Sopenharmony_ci * @index: Index of the interrupt to map 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * This function is a wrapper that chains of_irq_parse_one() and 3462306a36Sopenharmony_ci * irq_create_of_mapping() to make things easier to callers 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ciunsigned int irq_of_parse_and_map(struct device_node *dev, int index) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct of_phandle_args oirq; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (of_irq_parse_one(dev, index, &oirq)) 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return irq_create_of_mapping(&oirq); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(irq_of_parse_and_map); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * of_irq_find_parent - Given a device node, find its interrupt parent node 4962306a36Sopenharmony_ci * @child: pointer to device node 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Return: A pointer to the interrupt parent node, or NULL if the interrupt 5262306a36Sopenharmony_ci * parent could not be determined. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct device_node *of_irq_find_parent(struct device_node *child) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct device_node *p; 5762306a36Sopenharmony_ci phandle parent; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!of_node_get(child)) 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci do { 6362306a36Sopenharmony_ci if (of_property_read_u32(child, "interrupt-parent", &parent)) { 6462306a36Sopenharmony_ci p = of_get_parent(child); 6562306a36Sopenharmony_ci } else { 6662306a36Sopenharmony_ci if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) 6762306a36Sopenharmony_ci p = of_node_get(of_irq_dflt_pic); 6862306a36Sopenharmony_ci else 6962306a36Sopenharmony_ci p = of_find_node_by_phandle(parent); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci of_node_put(child); 7262306a36Sopenharmony_ci child = p; 7362306a36Sopenharmony_ci } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return p; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_find_parent); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * These interrupt controllers abuse interrupt-map for unspeakable 8162306a36Sopenharmony_ci * reasons and rely on the core code to *ignore* it (the drivers do 8262306a36Sopenharmony_ci * their own parsing of the property). 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * If you think of adding to the list for something *new*, think 8562306a36Sopenharmony_ci * again. There is a high chance that you will be sent back to the 8662306a36Sopenharmony_ci * drawing board. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic const char * const of_irq_imap_abusers[] = { 8962306a36Sopenharmony_ci "CBEA,platform-spider-pic", 9062306a36Sopenharmony_ci "sti,platform-spider-pic", 9162306a36Sopenharmony_ci "realtek,rtl-intc", 9262306a36Sopenharmony_ci "fsl,ls1021a-extirq", 9362306a36Sopenharmony_ci "fsl,ls1043a-extirq", 9462306a36Sopenharmony_ci "fsl,ls1088a-extirq", 9562306a36Sopenharmony_ci "renesas,rza1-irqc", 9662306a36Sopenharmony_ci NULL, 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/** 10062306a36Sopenharmony_ci * of_irq_parse_raw - Low level interrupt tree parsing 10162306a36Sopenharmony_ci * @addr: address specifier (start of "reg" property of the device) in be32 format 10262306a36Sopenharmony_ci * @out_irq: structure of_phandle_args updated by this function 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * This function is a low-level interrupt tree walking function. It 10562306a36Sopenharmony_ci * can be used to do a partial walk with synthetized reg and interrupts 10662306a36Sopenharmony_ci * properties, for example when resolving PCI interrupts when no device 10762306a36Sopenharmony_ci * node exist for the parent. It takes an interrupt specifier structure as 10862306a36Sopenharmony_ci * input, walks the tree looking for any interrupt-map properties, translates 10962306a36Sopenharmony_ci * the specifier for each map, and then returns the translated map. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * Return: 0 on success and a negative number on error 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ciint of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; 11662306a36Sopenharmony_ci __be32 initial_match_array[MAX_PHANDLE_ARGS]; 11762306a36Sopenharmony_ci const __be32 *match_array = initial_match_array; 11862306a36Sopenharmony_ci const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; 11962306a36Sopenharmony_ci u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; 12062306a36Sopenharmony_ci int imaplen, match, i, rc = -EINVAL; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#ifdef DEBUG 12362306a36Sopenharmony_ci of_print_phandle_args("of_irq_parse_raw: ", out_irq); 12462306a36Sopenharmony_ci#endif 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ipar = of_node_get(out_irq->np); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* First get the #interrupt-cells property of the current cursor 12962306a36Sopenharmony_ci * that tells us how to interpret the passed-in intspec. If there 13062306a36Sopenharmony_ci * is none, we are nice and just walk up the tree 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci do { 13362306a36Sopenharmony_ci if (!of_property_read_u32(ipar, "#interrupt-cells", &intsize)) 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci tnode = ipar; 13662306a36Sopenharmony_ci ipar = of_irq_find_parent(ipar); 13762306a36Sopenharmony_ci of_node_put(tnode); 13862306a36Sopenharmony_ci } while (ipar); 13962306a36Sopenharmony_ci if (ipar == NULL) { 14062306a36Sopenharmony_ci pr_debug(" -> no parent found !\n"); 14162306a36Sopenharmony_ci goto fail; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci pr_debug("of_irq_parse_raw: ipar=%pOF, size=%d\n", ipar, intsize); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (out_irq->args_count != intsize) 14762306a36Sopenharmony_ci goto fail; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Look for this #address-cells. We have to implement the old linux 15062306a36Sopenharmony_ci * trick of looking for the parent here as some device-trees rely on it 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci old = of_node_get(ipar); 15362306a36Sopenharmony_ci do { 15462306a36Sopenharmony_ci tmp = of_get_property(old, "#address-cells", NULL); 15562306a36Sopenharmony_ci tnode = of_get_parent(old); 15662306a36Sopenharmony_ci of_node_put(old); 15762306a36Sopenharmony_ci old = tnode; 15862306a36Sopenharmony_ci } while (old && tmp == NULL); 15962306a36Sopenharmony_ci of_node_put(old); 16062306a36Sopenharmony_ci old = NULL; 16162306a36Sopenharmony_ci addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci pr_debug(" -> addrsize=%d\n", addrsize); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Range check so that the temporary buffer doesn't overflow */ 16662306a36Sopenharmony_ci if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)) { 16762306a36Sopenharmony_ci rc = -EFAULT; 16862306a36Sopenharmony_ci goto fail; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Precalculate the match array - this simplifies match loop */ 17262306a36Sopenharmony_ci for (i = 0; i < addrsize; i++) 17362306a36Sopenharmony_ci initial_match_array[i] = addr ? addr[i] : 0; 17462306a36Sopenharmony_ci for (i = 0; i < intsize; i++) 17562306a36Sopenharmony_ci initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Now start the actual "proper" walk of the interrupt tree */ 17862306a36Sopenharmony_ci while (ipar != NULL) { 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * Now check if cursor is an interrupt-controller and 18162306a36Sopenharmony_ci * if it is then we are done, unless there is an 18262306a36Sopenharmony_ci * interrupt-map which takes precedence except on one 18362306a36Sopenharmony_ci * of these broken platforms that want to parse 18462306a36Sopenharmony_ci * interrupt-map themselves for $reason. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci bool intc = of_property_read_bool(ipar, "interrupt-controller"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci imap = of_get_property(ipar, "interrupt-map", &imaplen); 18962306a36Sopenharmony_ci if (intc && 19062306a36Sopenharmony_ci (!imap || of_device_compatible_match(ipar, of_irq_imap_abusers))) { 19162306a36Sopenharmony_ci pr_debug(" -> got it !\n"); 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * interrupt-map parsing does not work without a reg 19762306a36Sopenharmony_ci * property when #address-cells != 0 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci if (addrsize && !addr) { 20062306a36Sopenharmony_ci pr_debug(" -> no reg passed in when needed !\n"); 20162306a36Sopenharmony_ci goto fail; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* No interrupt map, check for an interrupt parent */ 20562306a36Sopenharmony_ci if (imap == NULL) { 20662306a36Sopenharmony_ci pr_debug(" -> no map, getting parent\n"); 20762306a36Sopenharmony_ci newpar = of_irq_find_parent(ipar); 20862306a36Sopenharmony_ci goto skiplevel; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci imaplen /= sizeof(u32); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Look for a mask */ 21362306a36Sopenharmony_ci imask = of_get_property(ipar, "interrupt-map-mask", NULL); 21462306a36Sopenharmony_ci if (!imask) 21562306a36Sopenharmony_ci imask = dummy_imask; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Parse interrupt-map */ 21862306a36Sopenharmony_ci match = 0; 21962306a36Sopenharmony_ci while (imaplen > (addrsize + intsize + 1) && !match) { 22062306a36Sopenharmony_ci /* Compare specifiers */ 22162306a36Sopenharmony_ci match = 1; 22262306a36Sopenharmony_ci for (i = 0; i < (addrsize + intsize); i++, imaplen--) 22362306a36Sopenharmony_ci match &= !((match_array[i] ^ *imap++) & imask[i]); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Get the interrupt parent */ 22862306a36Sopenharmony_ci if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) 22962306a36Sopenharmony_ci newpar = of_node_get(of_irq_dflt_pic); 23062306a36Sopenharmony_ci else 23162306a36Sopenharmony_ci newpar = of_find_node_by_phandle(be32_to_cpup(imap)); 23262306a36Sopenharmony_ci imap++; 23362306a36Sopenharmony_ci --imaplen; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Check if not found */ 23662306a36Sopenharmony_ci if (newpar == NULL) { 23762306a36Sopenharmony_ci pr_debug(" -> imap parent not found !\n"); 23862306a36Sopenharmony_ci goto fail; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!of_device_is_available(newpar)) 24262306a36Sopenharmony_ci match = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Get #interrupt-cells and #address-cells of new 24562306a36Sopenharmony_ci * parent 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci if (of_property_read_u32(newpar, "#interrupt-cells", 24862306a36Sopenharmony_ci &newintsize)) { 24962306a36Sopenharmony_ci pr_debug(" -> parent lacks #interrupt-cells!\n"); 25062306a36Sopenharmony_ci goto fail; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci if (of_property_read_u32(newpar, "#address-cells", 25362306a36Sopenharmony_ci &newaddrsize)) 25462306a36Sopenharmony_ci newaddrsize = 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci pr_debug(" -> newintsize=%d, newaddrsize=%d\n", 25762306a36Sopenharmony_ci newintsize, newaddrsize); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Check for malformed properties */ 26062306a36Sopenharmony_ci if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS) 26162306a36Sopenharmony_ci || (imaplen < (newaddrsize + newintsize))) { 26262306a36Sopenharmony_ci rc = -EFAULT; 26362306a36Sopenharmony_ci goto fail; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci imap += newaddrsize + newintsize; 26762306a36Sopenharmony_ci imaplen -= newaddrsize + newintsize; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pr_debug(" -> imaplen=%d\n", imaplen); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci if (!match) { 27262306a36Sopenharmony_ci if (intc) { 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * The PASEMI Nemo is a known offender, so 27562306a36Sopenharmony_ci * let's only warn for anyone else. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci WARN(!IS_ENABLED(CONFIG_PPC_PASEMI), 27862306a36Sopenharmony_ci "%pOF interrupt-map failed, using interrupt-controller\n", 27962306a36Sopenharmony_ci ipar); 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci goto fail; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * Successfully parsed an interrupt-map translation; copy new 28862306a36Sopenharmony_ci * interrupt specifier into the out_irq structure 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci match_array = imap - newaddrsize - newintsize; 29162306a36Sopenharmony_ci for (i = 0; i < newintsize; i++) 29262306a36Sopenharmony_ci out_irq->args[i] = be32_to_cpup(imap - newintsize + i); 29362306a36Sopenharmony_ci out_irq->args_count = intsize = newintsize; 29462306a36Sopenharmony_ci addrsize = newaddrsize; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (ipar == newpar) { 29762306a36Sopenharmony_ci pr_debug("%pOF interrupt-map entry to self\n", ipar); 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci skiplevel: 30262306a36Sopenharmony_ci /* Iterate again with new parent */ 30362306a36Sopenharmony_ci out_irq->np = newpar; 30462306a36Sopenharmony_ci pr_debug(" -> new parent: %pOF\n", newpar); 30562306a36Sopenharmony_ci of_node_put(ipar); 30662306a36Sopenharmony_ci ipar = newpar; 30762306a36Sopenharmony_ci newpar = NULL; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci rc = -ENOENT; /* No interrupt-map found */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci fail: 31262306a36Sopenharmony_ci of_node_put(ipar); 31362306a36Sopenharmony_ci of_node_put(newpar); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return rc; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_parse_raw); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/** 32062306a36Sopenharmony_ci * of_irq_parse_one - Resolve an interrupt for a device 32162306a36Sopenharmony_ci * @device: the device whose interrupt is to be resolved 32262306a36Sopenharmony_ci * @index: index of the interrupt to resolve 32362306a36Sopenharmony_ci * @out_irq: structure of_phandle_args filled by this function 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * This function resolves an interrupt for a node by walking the interrupt tree, 32662306a36Sopenharmony_ci * finding which interrupt controller node it is attached to, and returning the 32762306a36Sopenharmony_ci * interrupt specifier that can be used to retrieve a Linux IRQ number. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ciint of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct device_node *p; 33262306a36Sopenharmony_ci const __be32 *addr; 33362306a36Sopenharmony_ci u32 intsize; 33462306a36Sopenharmony_ci int i, res; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci pr_debug("of_irq_parse_one: dev=%pOF, index=%d\n", device, index); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* OldWorld mac stuff is "special", handle out of line */ 33962306a36Sopenharmony_ci if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) 34062306a36Sopenharmony_ci return of_irq_parse_oldworld(device, index, out_irq); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Get the reg property (if any) */ 34362306a36Sopenharmony_ci addr = of_get_property(device, "reg", NULL); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Try the new-style interrupts-extended first */ 34662306a36Sopenharmony_ci res = of_parse_phandle_with_args(device, "interrupts-extended", 34762306a36Sopenharmony_ci "#interrupt-cells", index, out_irq); 34862306a36Sopenharmony_ci if (!res) 34962306a36Sopenharmony_ci return of_irq_parse_raw(addr, out_irq); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Look for the interrupt parent. */ 35262306a36Sopenharmony_ci p = of_irq_find_parent(device); 35362306a36Sopenharmony_ci if (p == NULL) 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Get size of interrupt specifier */ 35762306a36Sopenharmony_ci if (of_property_read_u32(p, "#interrupt-cells", &intsize)) { 35862306a36Sopenharmony_ci res = -EINVAL; 35962306a36Sopenharmony_ci goto out; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci pr_debug(" parent=%pOF, intsize=%d\n", p, intsize); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Copy intspec into irq structure */ 36562306a36Sopenharmony_ci out_irq->np = p; 36662306a36Sopenharmony_ci out_irq->args_count = intsize; 36762306a36Sopenharmony_ci for (i = 0; i < intsize; i++) { 36862306a36Sopenharmony_ci res = of_property_read_u32_index(device, "interrupts", 36962306a36Sopenharmony_ci (index * intsize) + i, 37062306a36Sopenharmony_ci out_irq->args + i); 37162306a36Sopenharmony_ci if (res) 37262306a36Sopenharmony_ci goto out; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci pr_debug(" intspec=%d\n", *out_irq->args); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Check if there are any interrupt-map translations to process */ 37962306a36Sopenharmony_ci res = of_irq_parse_raw(addr, out_irq); 38062306a36Sopenharmony_ci out: 38162306a36Sopenharmony_ci of_node_put(p); 38262306a36Sopenharmony_ci return res; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_parse_one); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * of_irq_to_resource - Decode a node's IRQ and return it as a resource 38862306a36Sopenharmony_ci * @dev: pointer to device tree node 38962306a36Sopenharmony_ci * @index: zero-based index of the irq 39062306a36Sopenharmony_ci * @r: pointer to resource structure to return result into. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ciint of_irq_to_resource(struct device_node *dev, int index, struct resource *r) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int irq = of_irq_get(dev, index); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (irq < 0) 39762306a36Sopenharmony_ci return irq; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Only dereference the resource if both the 40062306a36Sopenharmony_ci * resource and the irq are valid. */ 40162306a36Sopenharmony_ci if (r && irq) { 40262306a36Sopenharmony_ci const char *name = NULL; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci memset(r, 0, sizeof(*r)); 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * Get optional "interrupt-names" property to add a name 40762306a36Sopenharmony_ci * to the resource. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci of_property_read_string_index(dev, "interrupt-names", index, 41062306a36Sopenharmony_ci &name); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci r->start = r->end = irq; 41362306a36Sopenharmony_ci r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq)); 41462306a36Sopenharmony_ci r->name = name ? name : of_node_full_name(dev); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return irq; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_to_resource); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/** 42262306a36Sopenharmony_ci * of_irq_get - Decode a node's IRQ and return it as a Linux IRQ number 42362306a36Sopenharmony_ci * @dev: pointer to device tree node 42462306a36Sopenharmony_ci * @index: zero-based index of the IRQ 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Return: Linux IRQ number on success, or 0 on the IRQ mapping failure, or 42762306a36Sopenharmony_ci * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case 42862306a36Sopenharmony_ci * of any other failure. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ciint of_irq_get(struct device_node *dev, int index) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int rc; 43362306a36Sopenharmony_ci struct of_phandle_args oirq; 43462306a36Sopenharmony_ci struct irq_domain *domain; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci rc = of_irq_parse_one(dev, index, &oirq); 43762306a36Sopenharmony_ci if (rc) 43862306a36Sopenharmony_ci return rc; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci domain = irq_find_host(oirq.np); 44162306a36Sopenharmony_ci if (!domain) { 44262306a36Sopenharmony_ci rc = -EPROBE_DEFER; 44362306a36Sopenharmony_ci goto out; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci rc = irq_create_of_mapping(&oirq); 44762306a36Sopenharmony_ciout: 44862306a36Sopenharmony_ci of_node_put(oirq.np); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return rc; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_get); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/** 45562306a36Sopenharmony_ci * of_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number 45662306a36Sopenharmony_ci * @dev: pointer to device tree node 45762306a36Sopenharmony_ci * @name: IRQ name 45862306a36Sopenharmony_ci * 45962306a36Sopenharmony_ci * Return: Linux IRQ number on success, or 0 on the IRQ mapping failure, or 46062306a36Sopenharmony_ci * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case 46162306a36Sopenharmony_ci * of any other failure. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ciint of_irq_get_byname(struct device_node *dev, const char *name) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int index; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (unlikely(!name)) 46862306a36Sopenharmony_ci return -EINVAL; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci index = of_property_match_string(dev, "interrupt-names", name); 47162306a36Sopenharmony_ci if (index < 0) 47262306a36Sopenharmony_ci return index; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return of_irq_get(dev, index); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_get_byname); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/** 47962306a36Sopenharmony_ci * of_irq_count - Count the number of IRQs a node uses 48062306a36Sopenharmony_ci * @dev: pointer to device tree node 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ciint of_irq_count(struct device_node *dev) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct of_phandle_args irq; 48562306a36Sopenharmony_ci int nr = 0; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci while (of_irq_parse_one(dev, nr, &irq) == 0) 48862306a36Sopenharmony_ci nr++; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return nr; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/** 49462306a36Sopenharmony_ci * of_irq_to_resource_table - Fill in resource table with node's IRQ info 49562306a36Sopenharmony_ci * @dev: pointer to device tree node 49662306a36Sopenharmony_ci * @res: array of resources to fill in 49762306a36Sopenharmony_ci * @nr_irqs: the number of IRQs (and upper bound for num of @res elements) 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Return: The size of the filled in table (up to @nr_irqs). 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ciint of_irq_to_resource_table(struct device_node *dev, struct resource *res, 50262306a36Sopenharmony_ci int nr_irqs) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci int i; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci for (i = 0; i < nr_irqs; i++, res++) 50762306a36Sopenharmony_ci if (of_irq_to_resource(dev, i, res) <= 0) 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return i; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_to_resource_table); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistruct of_intc_desc { 51562306a36Sopenharmony_ci struct list_head list; 51662306a36Sopenharmony_ci of_irq_init_cb_t irq_init_cb; 51762306a36Sopenharmony_ci struct device_node *dev; 51862306a36Sopenharmony_ci struct device_node *interrupt_parent; 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/** 52262306a36Sopenharmony_ci * of_irq_init - Scan and init matching interrupt controllers in DT 52362306a36Sopenharmony_ci * @matches: 0 terminated array of nodes to match and init function to call 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * This function scans the device tree for matching interrupt controller nodes, 52662306a36Sopenharmony_ci * and calls their initialization functions in order with parents first. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_civoid __init of_irq_init(const struct of_device_id *matches) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci const struct of_device_id *match; 53162306a36Sopenharmony_ci struct device_node *np, *parent = NULL; 53262306a36Sopenharmony_ci struct of_intc_desc *desc, *temp_desc; 53362306a36Sopenharmony_ci struct list_head intc_desc_list, intc_parent_list; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci INIT_LIST_HEAD(&intc_desc_list); 53662306a36Sopenharmony_ci INIT_LIST_HEAD(&intc_parent_list); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci for_each_matching_node_and_match(np, matches, &match) { 53962306a36Sopenharmony_ci if (!of_property_read_bool(np, "interrupt-controller") || 54062306a36Sopenharmony_ci !of_device_is_available(np)) 54162306a36Sopenharmony_ci continue; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (WARN(!match->data, "of_irq_init: no init function for %s\n", 54462306a36Sopenharmony_ci match->compatible)) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * Here, we allocate and populate an of_intc_desc with the node 54962306a36Sopenharmony_ci * pointer, interrupt-parent device_node etc. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 55262306a36Sopenharmony_ci if (!desc) { 55362306a36Sopenharmony_ci of_node_put(np); 55462306a36Sopenharmony_ci goto err; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci desc->irq_init_cb = match->data; 55862306a36Sopenharmony_ci desc->dev = of_node_get(np); 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * interrupts-extended can reference multiple parent domains. 56162306a36Sopenharmony_ci * Arbitrarily pick the first one; assume any other parents 56262306a36Sopenharmony_ci * are the same distance away from the root irq controller. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci desc->interrupt_parent = of_parse_phandle(np, "interrupts-extended", 0); 56562306a36Sopenharmony_ci if (!desc->interrupt_parent) 56662306a36Sopenharmony_ci desc->interrupt_parent = of_irq_find_parent(np); 56762306a36Sopenharmony_ci if (desc->interrupt_parent == np) { 56862306a36Sopenharmony_ci of_node_put(desc->interrupt_parent); 56962306a36Sopenharmony_ci desc->interrupt_parent = NULL; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci list_add_tail(&desc->list, &intc_desc_list); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * The root irq controller is the one without an interrupt-parent. 57662306a36Sopenharmony_ci * That one goes first, followed by the controllers that reference it, 57762306a36Sopenharmony_ci * followed by the ones that reference the 2nd level controllers, etc. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci while (!list_empty(&intc_desc_list)) { 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * Process all controllers with the current 'parent'. 58262306a36Sopenharmony_ci * First pass will be looking for NULL as the parent. 58362306a36Sopenharmony_ci * The assumption is that NULL parent means a root controller. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { 58662306a36Sopenharmony_ci int ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (desc->interrupt_parent != parent) 58962306a36Sopenharmony_ci continue; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci list_del(&desc->list); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci of_node_set_flag(desc->dev, OF_POPULATED); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci pr_debug("of_irq_init: init %pOF (%p), parent %p\n", 59662306a36Sopenharmony_ci desc->dev, 59762306a36Sopenharmony_ci desc->dev, desc->interrupt_parent); 59862306a36Sopenharmony_ci ret = desc->irq_init_cb(desc->dev, 59962306a36Sopenharmony_ci desc->interrupt_parent); 60062306a36Sopenharmony_ci if (ret) { 60162306a36Sopenharmony_ci pr_err("%s: Failed to init %pOF (%p), parent %p\n", 60262306a36Sopenharmony_ci __func__, desc->dev, desc->dev, 60362306a36Sopenharmony_ci desc->interrupt_parent); 60462306a36Sopenharmony_ci of_node_clear_flag(desc->dev, OF_POPULATED); 60562306a36Sopenharmony_ci kfree(desc); 60662306a36Sopenharmony_ci continue; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * This one is now set up; add it to the parent list so 61162306a36Sopenharmony_ci * its children can get processed in a subsequent pass. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci list_add_tail(&desc->list, &intc_parent_list); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Get the next pending parent that might have children */ 61762306a36Sopenharmony_ci desc = list_first_entry_or_null(&intc_parent_list, 61862306a36Sopenharmony_ci typeof(*desc), list); 61962306a36Sopenharmony_ci if (!desc) { 62062306a36Sopenharmony_ci pr_err("of_irq_init: children remain, but no parents\n"); 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci list_del(&desc->list); 62462306a36Sopenharmony_ci parent = desc->dev; 62562306a36Sopenharmony_ci kfree(desc); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) { 62962306a36Sopenharmony_ci list_del(&desc->list); 63062306a36Sopenharmony_ci kfree(desc); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_cierr: 63362306a36Sopenharmony_ci list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { 63462306a36Sopenharmony_ci list_del(&desc->list); 63562306a36Sopenharmony_ci of_node_put(desc->dev); 63662306a36Sopenharmony_ci kfree(desc); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic u32 __of_msi_map_id(struct device *dev, struct device_node **np, 64162306a36Sopenharmony_ci u32 id_in) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct device *parent_dev; 64462306a36Sopenharmony_ci u32 id_out = id_in; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * Walk up the device parent links looking for one with a 64862306a36Sopenharmony_ci * "msi-map" property. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ci for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) 65162306a36Sopenharmony_ci if (!of_map_id(parent_dev->of_node, id_in, "msi-map", 65262306a36Sopenharmony_ci "msi-map-mask", np, &id_out)) 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci return id_out; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/** 65862306a36Sopenharmony_ci * of_msi_map_id - Map a MSI ID for a device. 65962306a36Sopenharmony_ci * @dev: device for which the mapping is to be done. 66062306a36Sopenharmony_ci * @msi_np: device node of the expected msi controller. 66162306a36Sopenharmony_ci * @id_in: unmapped MSI ID for the device. 66262306a36Sopenharmony_ci * 66362306a36Sopenharmony_ci * Walk up the device hierarchy looking for devices with a "msi-map" 66462306a36Sopenharmony_ci * property. If found, apply the mapping to @id_in. 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * Return: The mapped MSI ID. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ciu32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci return __of_msi_map_id(dev, &msi_np, id_in); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci/** 67462306a36Sopenharmony_ci * of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain 67562306a36Sopenharmony_ci * @dev: device for which the mapping is to be done. 67662306a36Sopenharmony_ci * @id: Device ID. 67762306a36Sopenharmony_ci * @bus_token: Bus token 67862306a36Sopenharmony_ci * 67962306a36Sopenharmony_ci * Walk up the device hierarchy looking for devices with a "msi-map" 68062306a36Sopenharmony_ci * property. 68162306a36Sopenharmony_ci * 68262306a36Sopenharmony_ci * Returns: the MSI domain for this device (or NULL on failure) 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_cistruct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 id, 68562306a36Sopenharmony_ci u32 bus_token) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct device_node *np = NULL; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci __of_msi_map_id(dev, &np, id); 69062306a36Sopenharmony_ci return irq_find_matching_host(np, bus_token); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/** 69462306a36Sopenharmony_ci * of_msi_get_domain - Use msi-parent to find the relevant MSI domain 69562306a36Sopenharmony_ci * @dev: device for which the domain is requested 69662306a36Sopenharmony_ci * @np: device node for @dev 69762306a36Sopenharmony_ci * @token: bus type for this domain 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci * Parse the msi-parent property (both the simple and the complex 70062306a36Sopenharmony_ci * versions), and returns the corresponding MSI domain. 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * Returns: the MSI domain for this device (or NULL on failure). 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_cistruct irq_domain *of_msi_get_domain(struct device *dev, 70562306a36Sopenharmony_ci struct device_node *np, 70662306a36Sopenharmony_ci enum irq_domain_bus_token token) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct device_node *msi_np; 70962306a36Sopenharmony_ci struct irq_domain *d; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Check for a single msi-parent property */ 71262306a36Sopenharmony_ci msi_np = of_parse_phandle(np, "msi-parent", 0); 71362306a36Sopenharmony_ci if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) { 71462306a36Sopenharmony_ci d = irq_find_matching_host(msi_np, token); 71562306a36Sopenharmony_ci if (!d) 71662306a36Sopenharmony_ci of_node_put(msi_np); 71762306a36Sopenharmony_ci return d; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (token == DOMAIN_BUS_PLATFORM_MSI) { 72162306a36Sopenharmony_ci /* Check for the complex msi-parent version */ 72262306a36Sopenharmony_ci struct of_phandle_args args; 72362306a36Sopenharmony_ci int index = 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci while (!of_parse_phandle_with_args(np, "msi-parent", 72662306a36Sopenharmony_ci "#msi-cells", 72762306a36Sopenharmony_ci index, &args)) { 72862306a36Sopenharmony_ci d = irq_find_matching_host(args.np, token); 72962306a36Sopenharmony_ci if (d) 73062306a36Sopenharmony_ci return d; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci of_node_put(args.np); 73362306a36Sopenharmony_ci index++; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return NULL; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_msi_get_domain); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/** 74262306a36Sopenharmony_ci * of_msi_configure - Set the msi_domain field of a device 74362306a36Sopenharmony_ci * @dev: device structure to associate with an MSI irq domain 74462306a36Sopenharmony_ci * @np: device node for that device 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_civoid of_msi_configure(struct device *dev, struct device_node *np) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci dev_set_msi_domain(dev, 74962306a36Sopenharmony_ci of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI)); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_msi_configure); 752