11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2046d9ce6SRafael J. Wysocki /* 3046d9ce6SRafael J. Wysocki * drivers/acpi/resource.c - ACPI device resources interpretation. 4046d9ce6SRafael J. Wysocki * 5046d9ce6SRafael J. Wysocki * Copyright (C) 2012, Intel Corp. 6046d9ce6SRafael J. Wysocki * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 7046d9ce6SRafael J. Wysocki * 8046d9ce6SRafael J. Wysocki * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9046d9ce6SRafael J. Wysocki * 10046d9ce6SRafael J. Wysocki * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11046d9ce6SRafael J. Wysocki */ 12046d9ce6SRafael J. Wysocki 13046d9ce6SRafael J. Wysocki #include <linux/acpi.h> 14046d9ce6SRafael J. Wysocki #include <linux/device.h> 15046d9ce6SRafael J. Wysocki #include <linux/export.h> 16046d9ce6SRafael J. Wysocki #include <linux/ioport.h> 178e345c99SRafael J. Wysocki #include <linux/slab.h> 1855a93417SChristophe RICARD #include <linux/irq.h> 19892a0126SHui Wang #include <linux/dmi.h> 20046d9ce6SRafael J. Wysocki 21046d9ce6SRafael J. Wysocki #ifdef CONFIG_X86 22046d9ce6SRafael J. Wysocki #define valid_IRQ(i) (((i) != 0) && ((i) != 2)) 234a2e7aabSLorenzo Pieralisi static inline bool acpi_iospace_resource_valid(struct resource *res) 244a2e7aabSLorenzo Pieralisi { 254a2e7aabSLorenzo Pieralisi /* On X86 IO space is limited to the [0 - 64K] IO port range */ 264a2e7aabSLorenzo Pieralisi return res->end < 0x10003; 274a2e7aabSLorenzo Pieralisi } 28046d9ce6SRafael J. Wysocki #else 29046d9ce6SRafael J. Wysocki #define valid_IRQ(i) (true) 304a2e7aabSLorenzo Pieralisi /* 314a2e7aabSLorenzo Pieralisi * ACPI IO descriptors on arches other than X86 contain MMIO CPU physical 324a2e7aabSLorenzo Pieralisi * addresses mapping IO space in CPU physical address space, IO space 334a2e7aabSLorenzo Pieralisi * resources can be placed anywhere in the 64-bit physical address space. 344a2e7aabSLorenzo Pieralisi */ 354a2e7aabSLorenzo Pieralisi static inline bool 364a2e7aabSLorenzo Pieralisi acpi_iospace_resource_valid(struct resource *res) { return true; } 37046d9ce6SRafael J. Wysocki #endif 38046d9ce6SRafael J. Wysocki 39fa20b176SAgustin Vega-Frias #if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI) 40fa20b176SAgustin Vega-Frias static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq) 41fa20b176SAgustin Vega-Frias { 42fa20b176SAgustin Vega-Frias return ext_irq->resource_source.string_length == 0 && 43fa20b176SAgustin Vega-Frias ext_irq->producer_consumer == ACPI_CONSUMER; 44fa20b176SAgustin Vega-Frias } 45fa20b176SAgustin Vega-Frias #else 46fa20b176SAgustin Vega-Frias static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq) 47fa20b176SAgustin Vega-Frias { 48fa20b176SAgustin Vega-Frias return true; 49fa20b176SAgustin Vega-Frias } 50fa20b176SAgustin Vega-Frias #endif 51fa20b176SAgustin Vega-Frias 52c420dbd1SThomas Gleixner static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) 53046d9ce6SRafael J. Wysocki { 54c420dbd1SThomas Gleixner u64 reslen = end - start + 1; 55046d9ce6SRafael J. Wysocki 56c420dbd1SThomas Gleixner /* 57c420dbd1SThomas Gleixner * CHECKME: len might be required to check versus a minimum 58c420dbd1SThomas Gleixner * length as well. 1 for io is fine, but for memory it does 59c420dbd1SThomas Gleixner * not make any sense at all. 60aa714d28SJiang Liu * Note: some BIOSes report incorrect length for ACPI address space 61aa714d28SJiang Liu * descriptor, so remove check of 'reslen == len' to avoid regression. 62c420dbd1SThomas Gleixner */ 63aa714d28SJiang Liu if (len && reslen && start <= end) 64c420dbd1SThomas Gleixner return true; 65c420dbd1SThomas Gleixner 666a239af2SRafael J. Wysocki pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n", 67c420dbd1SThomas Gleixner io ? "io" : "mem", start, end, len); 68c420dbd1SThomas Gleixner 69c420dbd1SThomas Gleixner return false; 70c420dbd1SThomas Gleixner } 71c420dbd1SThomas Gleixner 72c420dbd1SThomas Gleixner static void acpi_dev_memresource_flags(struct resource *res, u64 len, 7372e26b0dSThomas Gleixner u8 write_protect) 74c420dbd1SThomas Gleixner { 75c420dbd1SThomas Gleixner res->flags = IORESOURCE_MEM; 76c420dbd1SThomas Gleixner 77c420dbd1SThomas Gleixner if (!acpi_dev_resource_len_valid(res->start, res->end, len, false)) 78c78b6885SJiang Liu res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 79046d9ce6SRafael J. Wysocki 80046d9ce6SRafael J. Wysocki if (write_protect == ACPI_READ_WRITE_MEMORY) 81c420dbd1SThomas Gleixner res->flags |= IORESOURCE_MEM_WRITEABLE; 82046d9ce6SRafael J. Wysocki } 83046d9ce6SRafael J. Wysocki 84046d9ce6SRafael J. Wysocki static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, 85046d9ce6SRafael J. Wysocki u8 write_protect) 86046d9ce6SRafael J. Wysocki { 87046d9ce6SRafael J. Wysocki res->start = start; 88046d9ce6SRafael J. Wysocki res->end = start + len - 1; 8972e26b0dSThomas Gleixner acpi_dev_memresource_flags(res, len, write_protect); 90046d9ce6SRafael J. Wysocki } 91046d9ce6SRafael J. Wysocki 92046d9ce6SRafael J. Wysocki /** 93046d9ce6SRafael J. Wysocki * acpi_dev_resource_memory - Extract ACPI memory resource information. 94046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 95046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 96046d9ce6SRafael J. Wysocki * 97046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents a memory resource and 98046d9ce6SRafael J. Wysocki * if that's the case, use the information in it to populate the generic 99046d9ce6SRafael J. Wysocki * resource object pointed to by @res. 100581c19f3SJiang Liu * 101581c19f3SJiang Liu * Return: 102581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 103581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 104581c19f3SJiang Liu * 3) true: valid assigned resource 105046d9ce6SRafael J. Wysocki */ 106046d9ce6SRafael J. Wysocki bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) 107046d9ce6SRafael J. Wysocki { 108046d9ce6SRafael J. Wysocki struct acpi_resource_memory24 *memory24; 109046d9ce6SRafael J. Wysocki struct acpi_resource_memory32 *memory32; 110046d9ce6SRafael J. Wysocki struct acpi_resource_fixed_memory32 *fixed_memory32; 111046d9ce6SRafael J. Wysocki 112046d9ce6SRafael J. Wysocki switch (ares->type) { 113046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_MEMORY24: 114046d9ce6SRafael J. Wysocki memory24 = &ares->data.memory24; 1158515f936SJiang Liu acpi_dev_get_memresource(res, memory24->minimum << 8, 1168515f936SJiang Liu memory24->address_length << 8, 117046d9ce6SRafael J. Wysocki memory24->write_protect); 118046d9ce6SRafael J. Wysocki break; 119046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_MEMORY32: 120046d9ce6SRafael J. Wysocki memory32 = &ares->data.memory32; 121046d9ce6SRafael J. Wysocki acpi_dev_get_memresource(res, memory32->minimum, 122046d9ce6SRafael J. Wysocki memory32->address_length, 123046d9ce6SRafael J. Wysocki memory32->write_protect); 124046d9ce6SRafael J. Wysocki break; 125046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 126046d9ce6SRafael J. Wysocki fixed_memory32 = &ares->data.fixed_memory32; 127046d9ce6SRafael J. Wysocki acpi_dev_get_memresource(res, fixed_memory32->address, 128046d9ce6SRafael J. Wysocki fixed_memory32->address_length, 129046d9ce6SRafael J. Wysocki fixed_memory32->write_protect); 130046d9ce6SRafael J. Wysocki break; 131046d9ce6SRafael J. Wysocki default: 132581c19f3SJiang Liu res->flags = 0; 133046d9ce6SRafael J. Wysocki return false; 134046d9ce6SRafael J. Wysocki } 135c420dbd1SThomas Gleixner 136c420dbd1SThomas Gleixner return !(res->flags & IORESOURCE_DISABLED); 137046d9ce6SRafael J. Wysocki } 138046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); 139046d9ce6SRafael J. Wysocki 1401d0420f1SThomas Gleixner static void acpi_dev_ioresource_flags(struct resource *res, u64 len, 14191236eccSJiang Liu u8 io_decode, u8 translation_type) 142046d9ce6SRafael J. Wysocki { 1431d0420f1SThomas Gleixner res->flags = IORESOURCE_IO; 1441d0420f1SThomas Gleixner 1451d0420f1SThomas Gleixner if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) 146c78b6885SJiang Liu res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 1471d0420f1SThomas Gleixner 1484a2e7aabSLorenzo Pieralisi if (!acpi_iospace_resource_valid(res)) 149c78b6885SJiang Liu res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 150046d9ce6SRafael J. Wysocki 151046d9ce6SRafael J. Wysocki if (io_decode == ACPI_DECODE_16) 1521d0420f1SThomas Gleixner res->flags |= IORESOURCE_IO_16BIT_ADDR; 15391236eccSJiang Liu if (translation_type == ACPI_SPARSE_TRANSLATION) 15491236eccSJiang Liu res->flags |= IORESOURCE_IO_SPARSE; 155046d9ce6SRafael J. Wysocki } 156046d9ce6SRafael J. Wysocki 157046d9ce6SRafael J. Wysocki static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, 158046d9ce6SRafael J. Wysocki u8 io_decode) 159046d9ce6SRafael J. Wysocki { 160046d9ce6SRafael J. Wysocki res->start = start; 1611d0420f1SThomas Gleixner res->end = start + len - 1; 16291236eccSJiang Liu acpi_dev_ioresource_flags(res, len, io_decode, 0); 163046d9ce6SRafael J. Wysocki } 164046d9ce6SRafael J. Wysocki 165046d9ce6SRafael J. Wysocki /** 166046d9ce6SRafael J. Wysocki * acpi_dev_resource_io - Extract ACPI I/O resource information. 167046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 168046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 169046d9ce6SRafael J. Wysocki * 170046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an I/O resource and 171046d9ce6SRafael J. Wysocki * if that's the case, use the information in it to populate the generic 172046d9ce6SRafael J. Wysocki * resource object pointed to by @res. 173581c19f3SJiang Liu * 174581c19f3SJiang Liu * Return: 175581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 176581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 177581c19f3SJiang Liu * 3) true: valid assigned resource 178046d9ce6SRafael J. Wysocki */ 179046d9ce6SRafael J. Wysocki bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) 180046d9ce6SRafael J. Wysocki { 181046d9ce6SRafael J. Wysocki struct acpi_resource_io *io; 182046d9ce6SRafael J. Wysocki struct acpi_resource_fixed_io *fixed_io; 183046d9ce6SRafael J. Wysocki 184046d9ce6SRafael J. Wysocki switch (ares->type) { 185046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_IO: 186046d9ce6SRafael J. Wysocki io = &ares->data.io; 187046d9ce6SRafael J. Wysocki acpi_dev_get_ioresource(res, io->minimum, 188046d9ce6SRafael J. Wysocki io->address_length, 189046d9ce6SRafael J. Wysocki io->io_decode); 190046d9ce6SRafael J. Wysocki break; 191046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_FIXED_IO: 192046d9ce6SRafael J. Wysocki fixed_io = &ares->data.fixed_io; 193046d9ce6SRafael J. Wysocki acpi_dev_get_ioresource(res, fixed_io->address, 194046d9ce6SRafael J. Wysocki fixed_io->address_length, 195046d9ce6SRafael J. Wysocki ACPI_DECODE_10); 196046d9ce6SRafael J. Wysocki break; 197046d9ce6SRafael J. Wysocki default: 198581c19f3SJiang Liu res->flags = 0; 199046d9ce6SRafael J. Wysocki return false; 200046d9ce6SRafael J. Wysocki } 2011d0420f1SThomas Gleixner 2021d0420f1SThomas Gleixner return !(res->flags & IORESOURCE_DISABLED); 203046d9ce6SRafael J. Wysocki } 204046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_io); 205046d9ce6SRafael J. Wysocki 206a49170b5SJiang Liu static bool acpi_decode_space(struct resource_win *win, 207eb76d55eSThomas Gleixner struct acpi_resource_address *addr, 208eb76d55eSThomas Gleixner struct acpi_address64_attribute *attr) 209eb76d55eSThomas Gleixner { 210eb76d55eSThomas Gleixner u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16; 211eb76d55eSThomas Gleixner bool wp = addr->info.mem.write_protect; 212eb76d55eSThomas Gleixner u64 len = attr->address_length; 2131fb01ca9SJiang Liu u64 start, end, offset = 0; 214a49170b5SJiang Liu struct resource *res = &win->res; 215eb76d55eSThomas Gleixner 216a274019fSJiang Liu /* 217a274019fSJiang Liu * Filter out invalid descriptor according to ACPI Spec 5.0, section 218a274019fSJiang Liu * 6.4.3.5 Address Space Resource Descriptors. 219a274019fSJiang Liu */ 220a274019fSJiang Liu if ((addr->min_address_fixed != addr->max_address_fixed && len) || 221a274019fSJiang Liu (addr->min_address_fixed && addr->max_address_fixed && !len)) 222a274019fSJiang Liu pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n", 223a274019fSJiang Liu addr->min_address_fixed, addr->max_address_fixed, len); 224a274019fSJiang Liu 2252ea3d266SJiang Liu /* 2262ea3d266SJiang Liu * For bridges that translate addresses across the bridge, 2272ea3d266SJiang Liu * translation_offset is the offset that must be added to the 2282ea3d266SJiang Liu * address on the secondary side to obtain the address on the 2292ea3d266SJiang Liu * primary side. Non-bridge devices must list 0 for all Address 2302ea3d266SJiang Liu * Translation offset bits. 2312ea3d266SJiang Liu */ 2321fb01ca9SJiang Liu if (addr->producer_consumer == ACPI_PRODUCER) 2331fb01ca9SJiang Liu offset = attr->translation_offset; 2341fb01ca9SJiang Liu else if (attr->translation_offset) 2352ea3d266SJiang Liu pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n", 2362ea3d266SJiang Liu attr->translation_offset); 2371fb01ca9SJiang Liu start = attr->minimum + offset; 2381fb01ca9SJiang Liu end = attr->maximum + offset; 2391fb01ca9SJiang Liu 2401fb01ca9SJiang Liu win->offset = offset; 2411fb01ca9SJiang Liu res->start = start; 2421fb01ca9SJiang Liu res->end = end; 2431fb01ca9SJiang Liu if (sizeof(resource_size_t) < sizeof(u64) && 2441fb01ca9SJiang Liu (offset != win->offset || start != res->start || end != res->end)) { 2451fb01ca9SJiang Liu pr_warn("acpi resource window ([%#llx-%#llx] ignored, not CPU addressable)\n", 2461fb01ca9SJiang Liu attr->minimum, attr->maximum); 2471fb01ca9SJiang Liu return false; 2482ea3d266SJiang Liu } 2492ea3d266SJiang Liu 250eb76d55eSThomas Gleixner switch (addr->resource_type) { 251eb76d55eSThomas Gleixner case ACPI_MEMORY_RANGE: 25272e26b0dSThomas Gleixner acpi_dev_memresource_flags(res, len, wp); 253eb76d55eSThomas Gleixner break; 254eb76d55eSThomas Gleixner case ACPI_IO_RANGE: 25591236eccSJiang Liu acpi_dev_ioresource_flags(res, len, iodec, 25691236eccSJiang Liu addr->info.io.translation_type); 257eb76d55eSThomas Gleixner break; 258eb76d55eSThomas Gleixner case ACPI_BUS_NUMBER_RANGE: 259eb76d55eSThomas Gleixner res->flags = IORESOURCE_BUS; 260eb76d55eSThomas Gleixner break; 261eb76d55eSThomas Gleixner default: 262eb76d55eSThomas Gleixner return false; 263eb76d55eSThomas Gleixner } 264eb76d55eSThomas Gleixner 26572e26b0dSThomas Gleixner if (addr->producer_consumer == ACPI_PRODUCER) 26672e26b0dSThomas Gleixner res->flags |= IORESOURCE_WINDOW; 26772e26b0dSThomas Gleixner 268fcb29bbcSThomas Gleixner if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY) 269fcb29bbcSThomas Gleixner res->flags |= IORESOURCE_PREFETCH; 270fcb29bbcSThomas Gleixner 271eb76d55eSThomas Gleixner return !(res->flags & IORESOURCE_DISABLED); 272eb76d55eSThomas Gleixner } 273eb76d55eSThomas Gleixner 274046d9ce6SRafael J. Wysocki /** 275046d9ce6SRafael J. Wysocki * acpi_dev_resource_address_space - Extract ACPI address space information. 276046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 277a49170b5SJiang Liu * @win: Output generic resource object. 278046d9ce6SRafael J. Wysocki * 279046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an address space resource 280046d9ce6SRafael J. Wysocki * and if that's the case, use the information in it to populate the generic 281a49170b5SJiang Liu * resource object pointed to by @win. 282581c19f3SJiang Liu * 283581c19f3SJiang Liu * Return: 284a49170b5SJiang Liu * 1) false with win->res.flags setting to zero: not the expected resource type 285a49170b5SJiang Liu * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned 286a49170b5SJiang Liu * resource 287581c19f3SJiang Liu * 3) true: valid assigned resource 288046d9ce6SRafael J. Wysocki */ 289046d9ce6SRafael J. Wysocki bool acpi_dev_resource_address_space(struct acpi_resource *ares, 290a49170b5SJiang Liu struct resource_win *win) 291046d9ce6SRafael J. Wysocki { 292046d9ce6SRafael J. Wysocki struct acpi_resource_address64 addr; 293046d9ce6SRafael J. Wysocki 294a49170b5SJiang Liu win->res.flags = 0; 295eb76d55eSThomas Gleixner if (ACPI_FAILURE(acpi_resource_to_address64(ares, &addr))) 2966658c739SJiang Liu return false; 297046d9ce6SRafael J. Wysocki 298a49170b5SJiang Liu return acpi_decode_space(win, (struct acpi_resource_address *)&addr, 299eb76d55eSThomas Gleixner &addr.address); 300046d9ce6SRafael J. Wysocki } 301046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space); 302046d9ce6SRafael J. Wysocki 303046d9ce6SRafael J. Wysocki /** 304046d9ce6SRafael J. Wysocki * acpi_dev_resource_ext_address_space - Extract ACPI address space information. 305046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 306a49170b5SJiang Liu * @win: Output generic resource object. 307046d9ce6SRafael J. Wysocki * 308046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an extended address space 309046d9ce6SRafael J. Wysocki * resource and if that's the case, use the information in it to populate the 310a49170b5SJiang Liu * generic resource object pointed to by @win. 311581c19f3SJiang Liu * 312581c19f3SJiang Liu * Return: 313a49170b5SJiang Liu * 1) false with win->res.flags setting to zero: not the expected resource type 314a49170b5SJiang Liu * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned 315a49170b5SJiang Liu * resource 316581c19f3SJiang Liu * 3) true: valid assigned resource 317046d9ce6SRafael J. Wysocki */ 318046d9ce6SRafael J. Wysocki bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, 319a49170b5SJiang Liu struct resource_win *win) 320046d9ce6SRafael J. Wysocki { 321046d9ce6SRafael J. Wysocki struct acpi_resource_extended_address64 *ext_addr; 322046d9ce6SRafael J. Wysocki 323a49170b5SJiang Liu win->res.flags = 0; 324046d9ce6SRafael J. Wysocki if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64) 325046d9ce6SRafael J. Wysocki return false; 326046d9ce6SRafael J. Wysocki 327046d9ce6SRafael J. Wysocki ext_addr = &ares->data.ext_address64; 328046d9ce6SRafael J. Wysocki 329a49170b5SJiang Liu return acpi_decode_space(win, (struct acpi_resource_address *)ext_addr, 330eb76d55eSThomas Gleixner &ext_addr->address); 331046d9ce6SRafael J. Wysocki } 332046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space); 333046d9ce6SRafael J. Wysocki 334046d9ce6SRafael J. Wysocki /** 335046d9ce6SRafael J. Wysocki * acpi_dev_irq_flags - Determine IRQ resource flags. 336046d9ce6SRafael J. Wysocki * @triggering: Triggering type as provided by ACPI. 337046d9ce6SRafael J. Wysocki * @polarity: Interrupt polarity as provided by ACPI. 338046d9ce6SRafael J. Wysocki * @shareable: Whether or not the interrupt is shareable. 3395ff81160SRaul E Rangel * @wake_capable: Wake capability as provided by ACPI. 340046d9ce6SRafael J. Wysocki */ 3415ff81160SRaul E Rangel unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable, u8 wake_capable) 342046d9ce6SRafael J. Wysocki { 343046d9ce6SRafael J. Wysocki unsigned long flags; 344046d9ce6SRafael J. Wysocki 345046d9ce6SRafael J. Wysocki if (triggering == ACPI_LEVEL_SENSITIVE) 346046d9ce6SRafael J. Wysocki flags = polarity == ACPI_ACTIVE_LOW ? 347046d9ce6SRafael J. Wysocki IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL; 348046d9ce6SRafael J. Wysocki else 349046d9ce6SRafael J. Wysocki flags = polarity == ACPI_ACTIVE_LOW ? 350046d9ce6SRafael J. Wysocki IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE; 351046d9ce6SRafael J. Wysocki 352046d9ce6SRafael J. Wysocki if (shareable == ACPI_SHARED) 353046d9ce6SRafael J. Wysocki flags |= IORESOURCE_IRQ_SHAREABLE; 354046d9ce6SRafael J. Wysocki 3555ff81160SRaul E Rangel if (wake_capable == ACPI_WAKE_CAPABLE) 3565ff81160SRaul E Rangel flags |= IORESOURCE_IRQ_WAKECAPABLE; 3575ff81160SRaul E Rangel 358046d9ce6SRafael J. Wysocki return flags | IORESOURCE_IRQ; 359046d9ce6SRafael J. Wysocki } 360046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_irq_flags); 361046d9ce6SRafael J. Wysocki 36255a93417SChristophe RICARD /** 36355a93417SChristophe RICARD * acpi_dev_get_irq_type - Determine irq type. 36455a93417SChristophe RICARD * @triggering: Triggering type as provided by ACPI. 36555a93417SChristophe RICARD * @polarity: Interrupt polarity as provided by ACPI. 36655a93417SChristophe RICARD */ 36755a93417SChristophe RICARD unsigned int acpi_dev_get_irq_type(int triggering, int polarity) 36855a93417SChristophe RICARD { 36955a93417SChristophe RICARD switch (polarity) { 37055a93417SChristophe RICARD case ACPI_ACTIVE_LOW: 37155a93417SChristophe RICARD return triggering == ACPI_EDGE_SENSITIVE ? 37255a93417SChristophe RICARD IRQ_TYPE_EDGE_FALLING : 37355a93417SChristophe RICARD IRQ_TYPE_LEVEL_LOW; 37455a93417SChristophe RICARD case ACPI_ACTIVE_HIGH: 37555a93417SChristophe RICARD return triggering == ACPI_EDGE_SENSITIVE ? 37655a93417SChristophe RICARD IRQ_TYPE_EDGE_RISING : 37755a93417SChristophe RICARD IRQ_TYPE_LEVEL_HIGH; 37855a93417SChristophe RICARD case ACPI_ACTIVE_BOTH: 37955a93417SChristophe RICARD if (triggering == ACPI_EDGE_SENSITIVE) 38055a93417SChristophe RICARD return IRQ_TYPE_EDGE_BOTH; 38157d2dd4bSGustavo A. R. Silva fallthrough; 38255a93417SChristophe RICARD default: 38355a93417SChristophe RICARD return IRQ_TYPE_NONE; 38455a93417SChristophe RICARD } 38555a93417SChristophe RICARD } 38655a93417SChristophe RICARD EXPORT_SYMBOL_GPL(acpi_dev_get_irq_type); 38755a93417SChristophe RICARD 388892a0126SHui Wang static const struct dmi_system_id medion_laptop[] = { 389892a0126SHui Wang { 390892a0126SHui Wang .ident = "MEDION P15651", 391892a0126SHui Wang .matches = { 392892a0126SHui Wang DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), 393892a0126SHui Wang DMI_MATCH(DMI_BOARD_NAME, "M15T"), 394892a0126SHui Wang }, 395892a0126SHui Wang }, 3961b26ae40SHui Wang { 3971b26ae40SHui Wang .ident = "MEDION S17405", 3981b26ae40SHui Wang .matches = { 3991b26ae40SHui Wang DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), 4001b26ae40SHui Wang DMI_MATCH(DMI_BOARD_NAME, "M17T"), 4011b26ae40SHui Wang }, 4021b26ae40SHui Wang }, 4032d0ab146SAymeric Wibo { 4042d0ab146SAymeric Wibo .ident = "MEDION S17413", 4052d0ab146SAymeric Wibo .matches = { 4062d0ab146SAymeric Wibo DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), 4072d0ab146SAymeric Wibo DMI_MATCH(DMI_BOARD_NAME, "M1xA"), 4082d0ab146SAymeric Wibo }, 4092d0ab146SAymeric Wibo }, 410892a0126SHui Wang { } 411892a0126SHui Wang }; 412892a0126SHui Wang 413e12dee37STamim Khan static const struct dmi_system_id asus_laptop[] = { 414e12dee37STamim Khan { 415e12dee37STamim Khan .ident = "Asus Vivobook K3402ZA", 416e12dee37STamim Khan .matches = { 417e12dee37STamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 418e12dee37STamim Khan DMI_MATCH(DMI_BOARD_NAME, "K3402ZA"), 419e12dee37STamim Khan }, 420e12dee37STamim Khan }, 421e12dee37STamim Khan { 422e12dee37STamim Khan .ident = "Asus Vivobook K3502ZA", 423e12dee37STamim Khan .matches = { 424e12dee37STamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 425e12dee37STamim Khan DMI_MATCH(DMI_BOARD_NAME, "K3502ZA"), 426e12dee37STamim Khan }, 427e12dee37STamim Khan }, 4286e5cbe7cSKellen Renshaw { 4296e5cbe7cSKellen Renshaw .ident = "Asus Vivobook S5402ZA", 4306e5cbe7cSKellen Renshaw .matches = { 4316e5cbe7cSKellen Renshaw DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 4326e5cbe7cSKellen Renshaw DMI_MATCH(DMI_BOARD_NAME, "S5402ZA"), 4336e5cbe7cSKellen Renshaw }, 4346e5cbe7cSKellen Renshaw }, 435b5f9223aSTamim Khan { 436b5f9223aSTamim Khan .ident = "Asus Vivobook S5602ZA", 437b5f9223aSTamim Khan .matches = { 438b5f9223aSTamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 439b5f9223aSTamim Khan DMI_MATCH(DMI_BOARD_NAME, "S5602ZA"), 440b5f9223aSTamim Khan }, 441b5f9223aSTamim Khan }, 4427203481fSHans de Goede { 44305cda427SPaul Menzel .ident = "Asus ExpertBook B1502CBA", 44405cda427SPaul Menzel .matches = { 44505cda427SPaul Menzel DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 44605cda427SPaul Menzel DMI_MATCH(DMI_BOARD_NAME, "B1502CBA"), 44705cda427SPaul Menzel }, 44805cda427SPaul Menzel }, 44905cda427SPaul Menzel { 45077c72488STamim Khan .ident = "Asus ExpertBook B2402CBA", 45177c72488STamim Khan .matches = { 45277c72488STamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 45377c72488STamim Khan DMI_MATCH(DMI_BOARD_NAME, "B2402CBA"), 45477c72488STamim Khan }, 45577c72488STamim Khan }, 45677c72488STamim Khan { 45765eb2867SVojtech Hejsek .ident = "Asus ExpertBook B2402FBA", 45865eb2867SVojtech Hejsek .matches = { 45965eb2867SVojtech Hejsek DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 46065eb2867SVojtech Hejsek DMI_MATCH(DMI_BOARD_NAME, "B2402FBA"), 46165eb2867SVojtech Hejsek }, 46265eb2867SVojtech Hejsek }, 46365eb2867SVojtech Hejsek { 4647203481fSHans de Goede .ident = "Asus ExpertBook B2502", 4657203481fSHans de Goede .matches = { 4667203481fSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 4677203481fSHans de Goede DMI_MATCH(DMI_BOARD_NAME, "B2502CBA"), 4687203481fSHans de Goede }, 4697203481fSHans de Goede }, 470e12dee37STamim Khan { } 471e12dee37STamim Khan }; 472e12dee37STamim Khan 473f3cb9b74SAdrian Freund static const struct dmi_system_id lenovo_laptop[] = { 474f3cb9b74SAdrian Freund { 475f3cb9b74SAdrian Freund .ident = "LENOVO IdeaPad Flex 5 14ALC7", 476f3cb9b74SAdrian Freund .matches = { 477f3cb9b74SAdrian Freund DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 478f3cb9b74SAdrian Freund DMI_MATCH(DMI_PRODUCT_NAME, "82R9"), 479f3cb9b74SAdrian Freund }, 480f3cb9b74SAdrian Freund }, 481bfcdf583SJiri Slaby (SUSE) { 482bfcdf583SJiri Slaby (SUSE) .ident = "LENOVO IdeaPad Flex 5 16ALC7", 483bfcdf583SJiri Slaby (SUSE) .matches = { 484bfcdf583SJiri Slaby (SUSE) DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 485bfcdf583SJiri Slaby (SUSE) DMI_MATCH(DMI_PRODUCT_NAME, "82RA"), 486bfcdf583SJiri Slaby (SUSE) }, 487bfcdf583SJiri Slaby (SUSE) }, 488bfcdf583SJiri Slaby (SUSE) { } 489bfcdf583SJiri Slaby (SUSE) }; 490bfcdf583SJiri Slaby (SUSE) 49117bb7046SWerner Sembach static const struct dmi_system_id tongfang_gm_rg[] = { 4927592b79bSErik Schumacher { 49317bb7046SWerner Sembach .ident = "TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD", 4947592b79bSErik Schumacher .matches = { 4957592b79bSErik Schumacher DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"), 4967592b79bSErik Schumacher }, 4977592b79bSErik Schumacher }, 4987592b79bSErik Schumacher { } 4997592b79bSErik Schumacher }; 5007592b79bSErik Schumacher 501cb18703cSAdam Niederer static const struct dmi_system_id maingear_laptop[] = { 502cb18703cSAdam Niederer { 503cb18703cSAdam Niederer .ident = "MAINGEAR Vector Pro 2 15", 504cb18703cSAdam Niederer .matches = { 505cb18703cSAdam Niederer DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"), 506cb18703cSAdam Niederer DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-15A3070T"), 507cb18703cSAdam Niederer } 508cb18703cSAdam Niederer }, 509cb18703cSAdam Niederer { 510cb18703cSAdam Niederer .ident = "MAINGEAR Vector Pro 2 17", 511cb18703cSAdam Niederer .matches = { 512cb18703cSAdam Niederer DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"), 513cb18703cSAdam Niederer DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-17A3070T"), 514cb18703cSAdam Niederer }, 515cb18703cSAdam Niederer }, 516cb18703cSAdam Niederer { } 517cb18703cSAdam Niederer }; 518cb18703cSAdam Niederer 519*71a48562SRubén Gómez static const struct dmi_system_id lg_laptop[] = { 520*71a48562SRubén Gómez { 521*71a48562SRubén Gómez .ident = "LG Electronics 17U70P", 522*71a48562SRubén Gómez .matches = { 523*71a48562SRubén Gómez DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), 524*71a48562SRubén Gómez DMI_MATCH(DMI_BOARD_NAME, "17U70P"), 525*71a48562SRubén Gómez }, 526*71a48562SRubén Gómez }, 527*71a48562SRubén Gómez { } 528*71a48562SRubén Gómez }; 529*71a48562SRubén Gómez 530892a0126SHui Wang struct irq_override_cmp { 531892a0126SHui Wang const struct dmi_system_id *system; 532892a0126SHui Wang unsigned char irq; 533892a0126SHui Wang unsigned char triggering; 534892a0126SHui Wang unsigned char polarity; 535892a0126SHui Wang unsigned char shareable; 536bfcdf583SJiri Slaby (SUSE) bool override; 537892a0126SHui Wang }; 538892a0126SHui Wang 539bfcdf583SJiri Slaby (SUSE) static const struct irq_override_cmp override_table[] = { 540bfcdf583SJiri Slaby (SUSE) { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 541bfcdf583SJiri Slaby (SUSE) { asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 542f3cb9b74SAdrian Freund { lenovo_laptop, 6, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true }, 543f3cb9b74SAdrian Freund { lenovo_laptop, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true }, 54417bb7046SWerner Sembach { tongfang_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, 545cb18703cSAdam Niederer { maingear_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, 546*71a48562SRubén Gómez { lg_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 547892a0126SHui Wang }; 548892a0126SHui Wang 549892a0126SHui Wang static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity, 550892a0126SHui Wang u8 shareable) 551892a0126SHui Wang { 552892a0126SHui Wang int i; 553892a0126SHui Wang 554bfcdf583SJiri Slaby (SUSE) for (i = 0; i < ARRAY_SIZE(override_table); i++) { 555bfcdf583SJiri Slaby (SUSE) const struct irq_override_cmp *entry = &override_table[i]; 556bfcdf583SJiri Slaby (SUSE) 557bfcdf583SJiri Slaby (SUSE) if (dmi_check_system(entry->system) && 558bfcdf583SJiri Slaby (SUSE) entry->irq == gsi && 559bfcdf583SJiri Slaby (SUSE) entry->triggering == triggering && 560bfcdf583SJiri Slaby (SUSE) entry->polarity == polarity && 561bfcdf583SJiri Slaby (SUSE) entry->shareable == shareable) 562bfcdf583SJiri Slaby (SUSE) return entry->override; 563bfcdf583SJiri Slaby (SUSE) } 564bfcdf583SJiri Slaby (SUSE) 5659946e39fSChuanhong Guo #ifdef CONFIG_X86 5669946e39fSChuanhong Guo /* 5679946e39fSChuanhong Guo * IRQ override isn't needed on modern AMD Zen systems and 5689946e39fSChuanhong Guo * this override breaks active low IRQs on AMD Ryzen 6000 and 5699946e39fSChuanhong Guo * newer systems. Skip it. 5709946e39fSChuanhong Guo */ 5719946e39fSChuanhong Guo if (boot_cpu_has(X86_FEATURE_ZEN)) 5729946e39fSChuanhong Guo return false; 5739946e39fSChuanhong Guo #endif 5749946e39fSChuanhong Guo 575892a0126SHui Wang return true; 576892a0126SHui Wang } 577892a0126SHui Wang 578046d9ce6SRafael J. Wysocki static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, 579204ebc0aSMika Westerberg u8 triggering, u8 polarity, u8 shareable, 5805ff81160SRaul E Rangel u8 wake_capable, bool check_override) 581046d9ce6SRafael J. Wysocki { 582046d9ce6SRafael J. Wysocki int irq, p, t; 583046d9ce6SRafael J. Wysocki 584046d9ce6SRafael J. Wysocki if (!valid_IRQ(gsi)) { 5851c3f69b4SJohn Garry irqresource_disabled(res, gsi); 586046d9ce6SRafael J. Wysocki return; 587046d9ce6SRafael J. Wysocki } 588046d9ce6SRafael J. Wysocki 589046d9ce6SRafael J. Wysocki /* 59003671057SMasahiro Yamada * In IO-APIC mode, use overridden attribute. Two reasons: 591046d9ce6SRafael J. Wysocki * 1. BIOS bug in DSDT 592046d9ce6SRafael J. Wysocki * 2. BIOS uses IO-APIC mode Interrupt Source Override 593204ebc0aSMika Westerberg * 594204ebc0aSMika Westerberg * We do this only if we are dealing with IRQ() or IRQNoFlags() 595204ebc0aSMika Westerberg * resource (the legacy ISA resources). With modern ACPI 5 devices 596204ebc0aSMika Westerberg * using extended IRQ descriptors we take the IRQ configuration 597204ebc0aSMika Westerberg * from _CRS directly. 598046d9ce6SRafael J. Wysocki */ 599892a0126SHui Wang if (check_override && 600892a0126SHui Wang acpi_dev_irq_override(gsi, triggering, polarity, shareable) && 601892a0126SHui Wang !acpi_get_override_irq(gsi, &t, &p)) { 602046d9ce6SRafael J. Wysocki u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; 603046d9ce6SRafael J. Wysocki u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; 604046d9ce6SRafael J. Wysocki 605046d9ce6SRafael J. Wysocki if (triggering != trig || polarity != pol) { 6064ef96d4dSJiri Slaby (SUSE) pr_warn("ACPI: IRQ %d override to %s%s, %s%s\n", gsi, 6074ef96d4dSJiri Slaby (SUSE) t ? "level" : "edge", 6084ef96d4dSJiri Slaby (SUSE) trig == triggering ? "" : "(!)", 6094ef96d4dSJiri Slaby (SUSE) p ? "low" : "high", 6104ef96d4dSJiri Slaby (SUSE) pol == polarity ? "" : "(!)"); 611046d9ce6SRafael J. Wysocki triggering = trig; 612046d9ce6SRafael J. Wysocki polarity = pol; 613046d9ce6SRafael J. Wysocki } 614046d9ce6SRafael J. Wysocki } 615046d9ce6SRafael J. Wysocki 6165ff81160SRaul E Rangel res->flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable); 617046d9ce6SRafael J. Wysocki irq = acpi_register_gsi(NULL, gsi, triggering, polarity); 618046d9ce6SRafael J. Wysocki if (irq >= 0) { 619046d9ce6SRafael J. Wysocki res->start = irq; 620046d9ce6SRafael J. Wysocki res->end = irq; 621046d9ce6SRafael J. Wysocki } else { 6221c3f69b4SJohn Garry irqresource_disabled(res, gsi); 623046d9ce6SRafael J. Wysocki } 624046d9ce6SRafael J. Wysocki } 625046d9ce6SRafael J. Wysocki 626046d9ce6SRafael J. Wysocki /** 627046d9ce6SRafael J. Wysocki * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information. 628046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 629046d9ce6SRafael J. Wysocki * @index: Index into the array of GSIs represented by the resource. 630046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 631046d9ce6SRafael J. Wysocki * 632046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an interrupt resource 633046d9ce6SRafael J. Wysocki * and @index does not exceed the resource's interrupt count (true is returned 634046d9ce6SRafael J. Wysocki * in that case regardless of the results of the other checks)). If that's the 635046d9ce6SRafael J. Wysocki * case, register the GSI corresponding to @index from the array of interrupts 636046d9ce6SRafael J. Wysocki * represented by the resource and populate the generic resource object pointed 637046d9ce6SRafael J. Wysocki * to by @res accordingly. If the registration of the GSI is not successful, 638046d9ce6SRafael J. Wysocki * IORESOURCE_DISABLED will be set it that object's flags. 639581c19f3SJiang Liu * 640581c19f3SJiang Liu * Return: 641581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 642581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 643581c19f3SJiang Liu * 3) true: valid assigned resource 644046d9ce6SRafael J. Wysocki */ 645046d9ce6SRafael J. Wysocki bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, 646046d9ce6SRafael J. Wysocki struct resource *res) 647046d9ce6SRafael J. Wysocki { 648046d9ce6SRafael J. Wysocki struct acpi_resource_irq *irq; 649046d9ce6SRafael J. Wysocki struct acpi_resource_extended_irq *ext_irq; 650046d9ce6SRafael J. Wysocki 651046d9ce6SRafael J. Wysocki switch (ares->type) { 652046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_IRQ: 653046d9ce6SRafael J. Wysocki /* 654046d9ce6SRafael J. Wysocki * Per spec, only one interrupt per descriptor is allowed in 655046d9ce6SRafael J. Wysocki * _CRS, but some firmware violates this, so parse them all. 656046d9ce6SRafael J. Wysocki */ 657046d9ce6SRafael J. Wysocki irq = &ares->data.irq; 658046d9ce6SRafael J. Wysocki if (index >= irq->interrupt_count) { 6591c3f69b4SJohn Garry irqresource_disabled(res, 0); 660046d9ce6SRafael J. Wysocki return false; 661046d9ce6SRafael J. Wysocki } 662046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, irq->interrupts[index], 663046d9ce6SRafael J. Wysocki irq->triggering, irq->polarity, 6645ff81160SRaul E Rangel irq->shareable, irq->wake_capable, 6655ff81160SRaul E Rangel true); 666046d9ce6SRafael J. Wysocki break; 667046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 668046d9ce6SRafael J. Wysocki ext_irq = &ares->data.extended_irq; 669046d9ce6SRafael J. Wysocki if (index >= ext_irq->interrupt_count) { 6701c3f69b4SJohn Garry irqresource_disabled(res, 0); 671046d9ce6SRafael J. Wysocki return false; 672046d9ce6SRafael J. Wysocki } 673fa20b176SAgustin Vega-Frias if (is_gsi(ext_irq)) 674046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, ext_irq->interrupts[index], 675046d9ce6SRafael J. Wysocki ext_irq->triggering, ext_irq->polarity, 6765ff81160SRaul E Rangel ext_irq->shareable, ext_irq->wake_capable, 6775ff81160SRaul E Rangel false); 678fa20b176SAgustin Vega-Frias else 6791c3f69b4SJohn Garry irqresource_disabled(res, 0); 680046d9ce6SRafael J. Wysocki break; 681046d9ce6SRafael J. Wysocki default: 682581c19f3SJiang Liu res->flags = 0; 683046d9ce6SRafael J. Wysocki return false; 684046d9ce6SRafael J. Wysocki } 685046d9ce6SRafael J. Wysocki 686046d9ce6SRafael J. Wysocki return true; 687046d9ce6SRafael J. Wysocki } 688046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); 6898e345c99SRafael J. Wysocki 6908e345c99SRafael J. Wysocki /** 6918e345c99SRafael J. Wysocki * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). 6928e345c99SRafael J. Wysocki * @list: The head of the resource list to free. 6938e345c99SRafael J. Wysocki */ 6948e345c99SRafael J. Wysocki void acpi_dev_free_resource_list(struct list_head *list) 6958e345c99SRafael J. Wysocki { 69690e97820SJiang Liu resource_list_free(list); 6978e345c99SRafael J. Wysocki } 6988e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); 6998e345c99SRafael J. Wysocki 7008e345c99SRafael J. Wysocki struct res_proc_context { 7018e345c99SRafael J. Wysocki struct list_head *list; 7028e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *); 7038e345c99SRafael J. Wysocki void *preproc_data; 7048e345c99SRafael J. Wysocki int count; 7058e345c99SRafael J. Wysocki int error; 7068e345c99SRafael J. Wysocki }; 7078e345c99SRafael J. Wysocki 708a49170b5SJiang Liu static acpi_status acpi_dev_new_resource_entry(struct resource_win *win, 7098e345c99SRafael J. Wysocki struct res_proc_context *c) 7108e345c99SRafael J. Wysocki { 71190e97820SJiang Liu struct resource_entry *rentry; 7128e345c99SRafael J. Wysocki 71390e97820SJiang Liu rentry = resource_list_create_entry(NULL, 0); 7148e345c99SRafael J. Wysocki if (!rentry) { 7158e345c99SRafael J. Wysocki c->error = -ENOMEM; 7168e345c99SRafael J. Wysocki return AE_NO_MEMORY; 7178e345c99SRafael J. Wysocki } 71890e97820SJiang Liu *rentry->res = win->res; 71993286f47SJiang Liu rentry->offset = win->offset; 72090e97820SJiang Liu resource_list_add_tail(rentry, c->list); 7218e345c99SRafael J. Wysocki c->count++; 7228e345c99SRafael J. Wysocki return AE_OK; 7238e345c99SRafael J. Wysocki } 7248e345c99SRafael J. Wysocki 7258e345c99SRafael J. Wysocki static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, 7268e345c99SRafael J. Wysocki void *context) 7278e345c99SRafael J. Wysocki { 7288e345c99SRafael J. Wysocki struct res_proc_context *c = context; 729a49170b5SJiang Liu struct resource_win win; 730a49170b5SJiang Liu struct resource *res = &win.res; 7318e345c99SRafael J. Wysocki int i; 7328e345c99SRafael J. Wysocki 7338e345c99SRafael J. Wysocki if (c->preproc) { 7348e345c99SRafael J. Wysocki int ret; 7358e345c99SRafael J. Wysocki 7368e345c99SRafael J. Wysocki ret = c->preproc(ares, c->preproc_data); 7378e345c99SRafael J. Wysocki if (ret < 0) { 7388e345c99SRafael J. Wysocki c->error = ret; 73912fc4dadSDaniel Scally return AE_ABORT_METHOD; 7408e345c99SRafael J. Wysocki } else if (ret > 0) { 7418e345c99SRafael J. Wysocki return AE_OK; 7428e345c99SRafael J. Wysocki } 7438e345c99SRafael J. Wysocki } 7448e345c99SRafael J. Wysocki 745a49170b5SJiang Liu memset(&win, 0, sizeof(win)); 7468e345c99SRafael J. Wysocki 747a49170b5SJiang Liu if (acpi_dev_resource_memory(ares, res) 748a49170b5SJiang Liu || acpi_dev_resource_io(ares, res) 749a49170b5SJiang Liu || acpi_dev_resource_address_space(ares, &win) 750a49170b5SJiang Liu || acpi_dev_resource_ext_address_space(ares, &win)) 751a49170b5SJiang Liu return acpi_dev_new_resource_entry(&win, c); 7528e345c99SRafael J. Wysocki 753a49170b5SJiang Liu for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) { 7548e345c99SRafael J. Wysocki acpi_status status; 7558e345c99SRafael J. Wysocki 756a49170b5SJiang Liu status = acpi_dev_new_resource_entry(&win, c); 7578e345c99SRafael J. Wysocki if (ACPI_FAILURE(status)) 7588e345c99SRafael J. Wysocki return status; 7598e345c99SRafael J. Wysocki } 7608e345c99SRafael J. Wysocki 7618e345c99SRafael J. Wysocki return AE_OK; 7628e345c99SRafael J. Wysocki } 7638e345c99SRafael J. Wysocki 7644f0450afSLorenzo Pieralisi static int __acpi_dev_get_resources(struct acpi_device *adev, 7654f0450afSLorenzo Pieralisi struct list_head *list, 7664f0450afSLorenzo Pieralisi int (*preproc)(struct acpi_resource *, void *), 7674f0450afSLorenzo Pieralisi void *preproc_data, char *method) 7684f0450afSLorenzo Pieralisi { 7694f0450afSLorenzo Pieralisi struct res_proc_context c; 7704f0450afSLorenzo Pieralisi acpi_status status; 7714f0450afSLorenzo Pieralisi 7724f0450afSLorenzo Pieralisi if (!adev || !adev->handle || !list_empty(list)) 7734f0450afSLorenzo Pieralisi return -EINVAL; 7744f0450afSLorenzo Pieralisi 7754f0450afSLorenzo Pieralisi if (!acpi_has_method(adev->handle, method)) 7764f0450afSLorenzo Pieralisi return 0; 7774f0450afSLorenzo Pieralisi 7784f0450afSLorenzo Pieralisi c.list = list; 7794f0450afSLorenzo Pieralisi c.preproc = preproc; 7804f0450afSLorenzo Pieralisi c.preproc_data = preproc_data; 7814f0450afSLorenzo Pieralisi c.count = 0; 7824f0450afSLorenzo Pieralisi c.error = 0; 7834f0450afSLorenzo Pieralisi status = acpi_walk_resources(adev->handle, method, 7844f0450afSLorenzo Pieralisi acpi_dev_process_resource, &c); 7854f0450afSLorenzo Pieralisi if (ACPI_FAILURE(status)) { 7864f0450afSLorenzo Pieralisi acpi_dev_free_resource_list(list); 7874f0450afSLorenzo Pieralisi return c.error ? c.error : -EIO; 7884f0450afSLorenzo Pieralisi } 7894f0450afSLorenzo Pieralisi 7904f0450afSLorenzo Pieralisi return c.count; 7914f0450afSLorenzo Pieralisi } 7924f0450afSLorenzo Pieralisi 7938e345c99SRafael J. Wysocki /** 7948e345c99SRafael J. Wysocki * acpi_dev_get_resources - Get current resources of a device. 7958e345c99SRafael J. Wysocki * @adev: ACPI device node to get the resources for. 7968e345c99SRafael J. Wysocki * @list: Head of the resultant list of resources (must be empty). 7978e345c99SRafael J. Wysocki * @preproc: The caller's preprocessing routine. 7988e345c99SRafael J. Wysocki * @preproc_data: Pointer passed to the caller's preprocessing routine. 7998e345c99SRafael J. Wysocki * 8008e345c99SRafael J. Wysocki * Evaluate the _CRS method for the given device node and process its output by 801935ab850STom Saeger * (1) executing the @preproc() routine provided by the caller, passing the 8028e345c99SRafael J. Wysocki * resource pointer and @preproc_data to it as arguments, for each ACPI resource 8038e345c99SRafael J. Wysocki * returned and (2) converting all of the returned ACPI resources into struct 8048e345c99SRafael J. Wysocki * resource objects if possible. If the return value of @preproc() in step (1) 8058e345c99SRafael J. Wysocki * is different from 0, step (2) is not applied to the given ACPI resource and 8068e345c99SRafael J. Wysocki * if that value is negative, the whole processing is aborted and that value is 8078e345c99SRafael J. Wysocki * returned as the final error code. 8088e345c99SRafael J. Wysocki * 8098e345c99SRafael J. Wysocki * The resultant struct resource objects are put on the list pointed to by 81090e97820SJiang Liu * @list, that must be empty initially, as members of struct resource_entry 8118e345c99SRafael J. Wysocki * objects. Callers of this routine should use %acpi_dev_free_resource_list() to 8128e345c99SRafael J. Wysocki * free that list. 8138e345c99SRafael J. Wysocki * 8148e345c99SRafael J. Wysocki * The number of resources in the output list is returned on success, an error 8158e345c99SRafael J. Wysocki * code reflecting the error condition is returned otherwise. 8168e345c99SRafael J. Wysocki */ 8178e345c99SRafael J. Wysocki int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, 8188e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *), 8198e345c99SRafael J. Wysocki void *preproc_data) 8208e345c99SRafael J. Wysocki { 8214f0450afSLorenzo Pieralisi return __acpi_dev_get_resources(adev, list, preproc, preproc_data, 8224f0450afSLorenzo Pieralisi METHOD_NAME__CRS); 8238e345c99SRafael J. Wysocki } 8248e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_get_resources); 82562d1141fSJiang Liu 826c04ac679SLorenzo Pieralisi static int is_memory(struct acpi_resource *ares, void *not_used) 827c04ac679SLorenzo Pieralisi { 828c04ac679SLorenzo Pieralisi struct resource_win win; 829c04ac679SLorenzo Pieralisi struct resource *res = &win.res; 830c04ac679SLorenzo Pieralisi 831c04ac679SLorenzo Pieralisi memset(&win, 0, sizeof(win)); 832c04ac679SLorenzo Pieralisi 8336d2b5a1cSHeikki Krogerus if (acpi_dev_filter_resource_type(ares, IORESOURCE_MEM)) 8346d2b5a1cSHeikki Krogerus return 1; 8356d2b5a1cSHeikki Krogerus 836c04ac679SLorenzo Pieralisi return !(acpi_dev_resource_memory(ares, res) 837c04ac679SLorenzo Pieralisi || acpi_dev_resource_address_space(ares, &win) 838c04ac679SLorenzo Pieralisi || acpi_dev_resource_ext_address_space(ares, &win)); 839c04ac679SLorenzo Pieralisi } 840c04ac679SLorenzo Pieralisi 841c04ac679SLorenzo Pieralisi /** 842c04ac679SLorenzo Pieralisi * acpi_dev_get_dma_resources - Get current DMA resources of a device. 843c04ac679SLorenzo Pieralisi * @adev: ACPI device node to get the resources for. 844c04ac679SLorenzo Pieralisi * @list: Head of the resultant list of resources (must be empty). 845c04ac679SLorenzo Pieralisi * 846c04ac679SLorenzo Pieralisi * Evaluate the _DMA method for the given device node and process its 847c04ac679SLorenzo Pieralisi * output. 848c04ac679SLorenzo Pieralisi * 849c04ac679SLorenzo Pieralisi * The resultant struct resource objects are put on the list pointed to 850c04ac679SLorenzo Pieralisi * by @list, that must be empty initially, as members of struct 851c04ac679SLorenzo Pieralisi * resource_entry objects. Callers of this routine should use 852c04ac679SLorenzo Pieralisi * %acpi_dev_free_resource_list() to free that list. 853c04ac679SLorenzo Pieralisi * 854c04ac679SLorenzo Pieralisi * The number of resources in the output list is returned on success, 855c04ac679SLorenzo Pieralisi * an error code reflecting the error condition is returned otherwise. 856c04ac679SLorenzo Pieralisi */ 857c04ac679SLorenzo Pieralisi int acpi_dev_get_dma_resources(struct acpi_device *adev, struct list_head *list) 858c04ac679SLorenzo Pieralisi { 859c04ac679SLorenzo Pieralisi return __acpi_dev_get_resources(adev, list, is_memory, NULL, 860c04ac679SLorenzo Pieralisi METHOD_NAME__DMA); 861c04ac679SLorenzo Pieralisi } 862c04ac679SLorenzo Pieralisi EXPORT_SYMBOL_GPL(acpi_dev_get_dma_resources); 863c04ac679SLorenzo Pieralisi 86462d1141fSJiang Liu /** 8656bb057bfSHeikki Krogerus * acpi_dev_get_memory_resources - Get current memory resources of a device. 8666bb057bfSHeikki Krogerus * @adev: ACPI device node to get the resources for. 8676bb057bfSHeikki Krogerus * @list: Head of the resultant list of resources (must be empty). 8686bb057bfSHeikki Krogerus * 8696bb057bfSHeikki Krogerus * This is a helper function that locates all memory type resources of @adev 8706bb057bfSHeikki Krogerus * with acpi_dev_get_resources(). 8716bb057bfSHeikki Krogerus * 8726bb057bfSHeikki Krogerus * The number of resources in the output list is returned on success, an error 8736bb057bfSHeikki Krogerus * code reflecting the error condition is returned otherwise. 8746bb057bfSHeikki Krogerus */ 8756bb057bfSHeikki Krogerus int acpi_dev_get_memory_resources(struct acpi_device *adev, struct list_head *list) 8766bb057bfSHeikki Krogerus { 8776bb057bfSHeikki Krogerus return acpi_dev_get_resources(adev, list, is_memory, NULL); 8786bb057bfSHeikki Krogerus } 8796bb057bfSHeikki Krogerus EXPORT_SYMBOL_GPL(acpi_dev_get_memory_resources); 8806bb057bfSHeikki Krogerus 8816bb057bfSHeikki Krogerus /** 88262d1141fSJiang Liu * acpi_dev_filter_resource_type - Filter ACPI resource according to resource 88362d1141fSJiang Liu * types 88462d1141fSJiang Liu * @ares: Input ACPI resource object. 88562d1141fSJiang Liu * @types: Valid resource types of IORESOURCE_XXX 88662d1141fSJiang Liu * 8872c62e849SJiang Liu * This is a helper function to support acpi_dev_get_resources(), which filters 88862d1141fSJiang Liu * ACPI resource objects according to resource types. 88962d1141fSJiang Liu */ 89062d1141fSJiang Liu int acpi_dev_filter_resource_type(struct acpi_resource *ares, 89162d1141fSJiang Liu unsigned long types) 89262d1141fSJiang Liu { 89362d1141fSJiang Liu unsigned long type = 0; 89462d1141fSJiang Liu 89562d1141fSJiang Liu switch (ares->type) { 89662d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY24: 89762d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY32: 89862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 89962d1141fSJiang Liu type = IORESOURCE_MEM; 90062d1141fSJiang Liu break; 90162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IO: 90262d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_IO: 90362d1141fSJiang Liu type = IORESOURCE_IO; 90462d1141fSJiang Liu break; 90562d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IRQ: 90662d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 90762d1141fSJiang Liu type = IORESOURCE_IRQ; 90862d1141fSJiang Liu break; 90962d1141fSJiang Liu case ACPI_RESOURCE_TYPE_DMA: 91062d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_DMA: 91162d1141fSJiang Liu type = IORESOURCE_DMA; 91262d1141fSJiang Liu break; 91362d1141fSJiang Liu case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 91462d1141fSJiang Liu type = IORESOURCE_REG; 91562d1141fSJiang Liu break; 91662d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS16: 91762d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS32: 91862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS64: 91962d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 92062d1141fSJiang Liu if (ares->data.address.resource_type == ACPI_MEMORY_RANGE) 92162d1141fSJiang Liu type = IORESOURCE_MEM; 92262d1141fSJiang Liu else if (ares->data.address.resource_type == ACPI_IO_RANGE) 92362d1141fSJiang Liu type = IORESOURCE_IO; 92462d1141fSJiang Liu else if (ares->data.address.resource_type == 92562d1141fSJiang Liu ACPI_BUS_NUMBER_RANGE) 92662d1141fSJiang Liu type = IORESOURCE_BUS; 92762d1141fSJiang Liu break; 92862d1141fSJiang Liu default: 92962d1141fSJiang Liu break; 93062d1141fSJiang Liu } 93162d1141fSJiang Liu 93262d1141fSJiang Liu return (type & types) ? 0 : 1; 93362d1141fSJiang Liu } 93462d1141fSJiang Liu EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); 93500710984SBjorn Helgaas 93600710984SBjorn Helgaas static int acpi_dev_consumes_res(struct acpi_device *adev, struct resource *res) 93700710984SBjorn Helgaas { 93800710984SBjorn Helgaas struct list_head resource_list; 93900710984SBjorn Helgaas struct resource_entry *rentry; 94000710984SBjorn Helgaas int ret, found = 0; 94100710984SBjorn Helgaas 94200710984SBjorn Helgaas INIT_LIST_HEAD(&resource_list); 94300710984SBjorn Helgaas ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 94400710984SBjorn Helgaas if (ret < 0) 94500710984SBjorn Helgaas return 0; 94600710984SBjorn Helgaas 94700710984SBjorn Helgaas list_for_each_entry(rentry, &resource_list, node) { 94800710984SBjorn Helgaas if (resource_contains(rentry->res, res)) { 94900710984SBjorn Helgaas found = 1; 95000710984SBjorn Helgaas break; 95100710984SBjorn Helgaas } 95200710984SBjorn Helgaas 95300710984SBjorn Helgaas } 95400710984SBjorn Helgaas 95500710984SBjorn Helgaas acpi_dev_free_resource_list(&resource_list); 95600710984SBjorn Helgaas return found; 95700710984SBjorn Helgaas } 95800710984SBjorn Helgaas 95900710984SBjorn Helgaas static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, 96000710984SBjorn Helgaas void *context, void **ret) 96100710984SBjorn Helgaas { 96200710984SBjorn Helgaas struct resource *res = context; 96300710984SBjorn Helgaas struct acpi_device **consumer = (struct acpi_device **) ret; 96499ece713SRafael J. Wysocki struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 96500710984SBjorn Helgaas 96699ece713SRafael J. Wysocki if (!adev) 96700710984SBjorn Helgaas return AE_OK; 96800710984SBjorn Helgaas 96900710984SBjorn Helgaas if (acpi_dev_consumes_res(adev, res)) { 97000710984SBjorn Helgaas *consumer = adev; 97100710984SBjorn Helgaas return AE_CTRL_TERMINATE; 97200710984SBjorn Helgaas } 97300710984SBjorn Helgaas 97400710984SBjorn Helgaas return AE_OK; 97500710984SBjorn Helgaas } 97600710984SBjorn Helgaas 97700710984SBjorn Helgaas /** 97800710984SBjorn Helgaas * acpi_resource_consumer - Find the ACPI device that consumes @res. 97900710984SBjorn Helgaas * @res: Resource to search for. 98000710984SBjorn Helgaas * 98100710984SBjorn Helgaas * Search the current resource settings (_CRS) of every ACPI device node 98200710984SBjorn Helgaas * for @res. If we find an ACPI device whose _CRS includes @res, return 98300710984SBjorn Helgaas * it. Otherwise, return NULL. 98400710984SBjorn Helgaas */ 98500710984SBjorn Helgaas struct acpi_device *acpi_resource_consumer(struct resource *res) 98600710984SBjorn Helgaas { 98700710984SBjorn Helgaas struct acpi_device *consumer = NULL; 98800710984SBjorn Helgaas 98900710984SBjorn Helgaas acpi_get_devices(NULL, acpi_res_consumer_cb, res, (void **) &consumer); 99000710984SBjorn Helgaas return consumer; 99100710984SBjorn Helgaas } 992