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 { 443*c1ed7217SHans de Goede .ident = "Asus ExpertBook B1402CBA", 444*c1ed7217SHans de Goede .matches = { 445*c1ed7217SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 446*c1ed7217SHans de Goede DMI_MATCH(DMI_BOARD_NAME, "B1402CBA"), 447*c1ed7217SHans de Goede }, 448*c1ed7217SHans de Goede }, 449*c1ed7217SHans de Goede { 45005cda427SPaul Menzel .ident = "Asus ExpertBook B1502CBA", 45105cda427SPaul Menzel .matches = { 45205cda427SPaul Menzel DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 45305cda427SPaul Menzel DMI_MATCH(DMI_BOARD_NAME, "B1502CBA"), 45405cda427SPaul Menzel }, 45505cda427SPaul Menzel }, 45605cda427SPaul Menzel { 45777c72488STamim Khan .ident = "Asus ExpertBook B2402CBA", 45877c72488STamim Khan .matches = { 45977c72488STamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 46077c72488STamim Khan DMI_MATCH(DMI_BOARD_NAME, "B2402CBA"), 46177c72488STamim Khan }, 46277c72488STamim Khan }, 46377c72488STamim Khan { 46465eb2867SVojtech Hejsek .ident = "Asus ExpertBook B2402FBA", 46565eb2867SVojtech Hejsek .matches = { 46665eb2867SVojtech Hejsek DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 46765eb2867SVojtech Hejsek DMI_MATCH(DMI_BOARD_NAME, "B2402FBA"), 46865eb2867SVojtech Hejsek }, 46965eb2867SVojtech Hejsek }, 47065eb2867SVojtech Hejsek { 4717203481fSHans de Goede .ident = "Asus ExpertBook B2502", 4727203481fSHans de Goede .matches = { 4737203481fSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 4747203481fSHans de Goede DMI_MATCH(DMI_BOARD_NAME, "B2502CBA"), 4757203481fSHans de Goede }, 4767203481fSHans de Goede }, 477e12dee37STamim Khan { } 478e12dee37STamim Khan }; 479e12dee37STamim Khan 4802d331a6aSHans de Goede static const struct dmi_system_id tongfang_gm_rg[] = { 4812d331a6aSHans de Goede { 4822d331a6aSHans de Goede .ident = "TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD", 4832d331a6aSHans de Goede .matches = { 4842d331a6aSHans de Goede DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"), 4852d331a6aSHans de Goede }, 4862d331a6aSHans de Goede }, 4872d331a6aSHans de Goede { } 4882d331a6aSHans de Goede }; 4892d331a6aSHans de Goede 4902d331a6aSHans de Goede static const struct dmi_system_id maingear_laptop[] = { 4912d331a6aSHans de Goede { 4922d331a6aSHans de Goede .ident = "MAINGEAR Vector Pro 2 15", 4932d331a6aSHans de Goede .matches = { 4942d331a6aSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"), 4952d331a6aSHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-15A3070T"), 4962d331a6aSHans de Goede } 4972d331a6aSHans de Goede }, 4982d331a6aSHans de Goede { 4992d331a6aSHans de Goede .ident = "MAINGEAR Vector Pro 2 17", 5002d331a6aSHans de Goede .matches = { 5012d331a6aSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"), 5022d331a6aSHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-17A3070T"), 5032d331a6aSHans de Goede }, 5042d331a6aSHans de Goede }, 5052d331a6aSHans de Goede { } 5062d331a6aSHans de Goede }; 5072d331a6aSHans de Goede 50856fec005SHans de Goede static const struct dmi_system_id pcspecialist_laptop[] = { 50956fec005SHans de Goede { 51056fec005SHans de Goede .ident = "PCSpecialist Elimina Pro 16 M", 511453b014eSHans de Goede /* 512453b014eSHans de Goede * Some models have product-name "Elimina Pro 16 M", 513453b014eSHans de Goede * others "GM6BGEQ". Match on board-name to match both. 514453b014eSHans de Goede */ 51556fec005SHans de Goede .matches = { 51656fec005SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "PCSpecialist"), 517453b014eSHans de Goede DMI_MATCH(DMI_BOARD_NAME, "GM6BGEQ"), 51856fec005SHans de Goede }, 51956fec005SHans de Goede }, 52056fec005SHans de Goede { } 52156fec005SHans de Goede }; 52256fec005SHans de Goede 52371a48562SRubén Gómez static const struct dmi_system_id lg_laptop[] = { 52471a48562SRubén Gómez { 52571a48562SRubén Gómez .ident = "LG Electronics 17U70P", 52671a48562SRubén Gómez .matches = { 52771a48562SRubén Gómez DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), 52871a48562SRubén Gómez DMI_MATCH(DMI_BOARD_NAME, "17U70P"), 52971a48562SRubén Gómez }, 53071a48562SRubén Gómez }, 53171a48562SRubén Gómez { } 53271a48562SRubén Gómez }; 53371a48562SRubén Gómez 534892a0126SHui Wang struct irq_override_cmp { 535892a0126SHui Wang const struct dmi_system_id *system; 536892a0126SHui Wang unsigned char irq; 537892a0126SHui Wang unsigned char triggering; 538892a0126SHui Wang unsigned char polarity; 539892a0126SHui Wang unsigned char shareable; 540bfcdf583SJiri Slaby (SUSE) bool override; 541892a0126SHui Wang }; 542892a0126SHui Wang 543bfcdf583SJiri Slaby (SUSE) static const struct irq_override_cmp override_table[] = { 544bfcdf583SJiri Slaby (SUSE) { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 545bfcdf583SJiri Slaby (SUSE) { asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 5462d331a6aSHans de Goede { tongfang_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, 5472d331a6aSHans de Goede { maingear_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, 54856fec005SHans de Goede { pcspecialist_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, 54971a48562SRubén Gómez { lg_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 550892a0126SHui Wang }; 551892a0126SHui Wang 552892a0126SHui Wang static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity, 553892a0126SHui Wang u8 shareable) 554892a0126SHui Wang { 555892a0126SHui Wang int i; 556892a0126SHui Wang 557bfcdf583SJiri Slaby (SUSE) for (i = 0; i < ARRAY_SIZE(override_table); i++) { 558bfcdf583SJiri Slaby (SUSE) const struct irq_override_cmp *entry = &override_table[i]; 559bfcdf583SJiri Slaby (SUSE) 560bfcdf583SJiri Slaby (SUSE) if (dmi_check_system(entry->system) && 561bfcdf583SJiri Slaby (SUSE) entry->irq == gsi && 562bfcdf583SJiri Slaby (SUSE) entry->triggering == triggering && 563bfcdf583SJiri Slaby (SUSE) entry->polarity == polarity && 564bfcdf583SJiri Slaby (SUSE) entry->shareable == shareable) 565bfcdf583SJiri Slaby (SUSE) return entry->override; 566bfcdf583SJiri Slaby (SUSE) } 567bfcdf583SJiri Slaby (SUSE) 5682d331a6aSHans de Goede #ifdef CONFIG_X86 5692d331a6aSHans de Goede /* 5709728ac22SHans de Goede * Always use the MADT override info, except for the i8042 PS/2 ctrl 5719728ac22SHans de Goede * IRQs (1 and 12). For these the DSDT IRQ settings should sometimes 5729728ac22SHans de Goede * be used otherwise PS/2 keyboards / mice will not work. 5739728ac22SHans de Goede */ 5749728ac22SHans de Goede if (gsi != 1 && gsi != 12) 5759728ac22SHans de Goede return true; 5769728ac22SHans de Goede 577c6a1fd91SHans de Goede /* If the override comes from an INT_SRC_OVR MADT entry, honor it. */ 578c6a1fd91SHans de Goede if (acpi_int_src_ovr[gsi]) 579c6a1fd91SHans de Goede return true; 580c6a1fd91SHans de Goede 5819728ac22SHans de Goede /* 5822d331a6aSHans de Goede * IRQ override isn't needed on modern AMD Zen systems and 5832d331a6aSHans de Goede * this override breaks active low IRQs on AMD Ryzen 6000 and 5842d331a6aSHans de Goede * newer systems. Skip it. 5852d331a6aSHans de Goede */ 5862d331a6aSHans de Goede if (boot_cpu_has(X86_FEATURE_ZEN)) 5872d331a6aSHans de Goede return false; 5882d331a6aSHans de Goede #endif 5892d331a6aSHans de Goede 590892a0126SHui Wang return true; 591892a0126SHui Wang } 592892a0126SHui Wang 593046d9ce6SRafael J. Wysocki static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, 594204ebc0aSMika Westerberg u8 triggering, u8 polarity, u8 shareable, 5955ff81160SRaul E Rangel u8 wake_capable, bool check_override) 596046d9ce6SRafael J. Wysocki { 597046d9ce6SRafael J. Wysocki int irq, p, t; 598046d9ce6SRafael J. Wysocki 599046d9ce6SRafael J. Wysocki if (!valid_IRQ(gsi)) { 6001c3f69b4SJohn Garry irqresource_disabled(res, gsi); 601046d9ce6SRafael J. Wysocki return; 602046d9ce6SRafael J. Wysocki } 603046d9ce6SRafael J. Wysocki 604046d9ce6SRafael J. Wysocki /* 60503671057SMasahiro Yamada * In IO-APIC mode, use overridden attribute. Two reasons: 606046d9ce6SRafael J. Wysocki * 1. BIOS bug in DSDT 607046d9ce6SRafael J. Wysocki * 2. BIOS uses IO-APIC mode Interrupt Source Override 608204ebc0aSMika Westerberg * 609204ebc0aSMika Westerberg * We do this only if we are dealing with IRQ() or IRQNoFlags() 610204ebc0aSMika Westerberg * resource (the legacy ISA resources). With modern ACPI 5 devices 611204ebc0aSMika Westerberg * using extended IRQ descriptors we take the IRQ configuration 612204ebc0aSMika Westerberg * from _CRS directly. 613046d9ce6SRafael J. Wysocki */ 614892a0126SHui Wang if (check_override && 615892a0126SHui Wang acpi_dev_irq_override(gsi, triggering, polarity, shareable) && 616892a0126SHui Wang !acpi_get_override_irq(gsi, &t, &p)) { 617046d9ce6SRafael J. Wysocki u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; 618046d9ce6SRafael J. Wysocki u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; 619046d9ce6SRafael J. Wysocki 620046d9ce6SRafael J. Wysocki if (triggering != trig || polarity != pol) { 6214ef96d4dSJiri Slaby (SUSE) pr_warn("ACPI: IRQ %d override to %s%s, %s%s\n", gsi, 6224ef96d4dSJiri Slaby (SUSE) t ? "level" : "edge", 6234ef96d4dSJiri Slaby (SUSE) trig == triggering ? "" : "(!)", 6244ef96d4dSJiri Slaby (SUSE) p ? "low" : "high", 6254ef96d4dSJiri Slaby (SUSE) pol == polarity ? "" : "(!)"); 626046d9ce6SRafael J. Wysocki triggering = trig; 627046d9ce6SRafael J. Wysocki polarity = pol; 628046d9ce6SRafael J. Wysocki } 629046d9ce6SRafael J. Wysocki } 630046d9ce6SRafael J. Wysocki 6315ff81160SRaul E Rangel res->flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable); 632046d9ce6SRafael J. Wysocki irq = acpi_register_gsi(NULL, gsi, triggering, polarity); 633046d9ce6SRafael J. Wysocki if (irq >= 0) { 634046d9ce6SRafael J. Wysocki res->start = irq; 635046d9ce6SRafael J. Wysocki res->end = irq; 636046d9ce6SRafael J. Wysocki } else { 6371c3f69b4SJohn Garry irqresource_disabled(res, gsi); 638046d9ce6SRafael J. Wysocki } 639046d9ce6SRafael J. Wysocki } 640046d9ce6SRafael J. Wysocki 641046d9ce6SRafael J. Wysocki /** 642046d9ce6SRafael J. Wysocki * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information. 643046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 644046d9ce6SRafael J. Wysocki * @index: Index into the array of GSIs represented by the resource. 645046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 646046d9ce6SRafael J. Wysocki * 647046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an interrupt resource 648046d9ce6SRafael J. Wysocki * and @index does not exceed the resource's interrupt count (true is returned 649046d9ce6SRafael J. Wysocki * in that case regardless of the results of the other checks)). If that's the 650046d9ce6SRafael J. Wysocki * case, register the GSI corresponding to @index from the array of interrupts 651046d9ce6SRafael J. Wysocki * represented by the resource and populate the generic resource object pointed 652046d9ce6SRafael J. Wysocki * to by @res accordingly. If the registration of the GSI is not successful, 653046d9ce6SRafael J. Wysocki * IORESOURCE_DISABLED will be set it that object's flags. 654581c19f3SJiang Liu * 655581c19f3SJiang Liu * Return: 656581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 657581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 658581c19f3SJiang Liu * 3) true: valid assigned resource 659046d9ce6SRafael J. Wysocki */ 660046d9ce6SRafael J. Wysocki bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, 661046d9ce6SRafael J. Wysocki struct resource *res) 662046d9ce6SRafael J. Wysocki { 663046d9ce6SRafael J. Wysocki struct acpi_resource_irq *irq; 664046d9ce6SRafael J. Wysocki struct acpi_resource_extended_irq *ext_irq; 665046d9ce6SRafael J. Wysocki 666046d9ce6SRafael J. Wysocki switch (ares->type) { 667046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_IRQ: 668046d9ce6SRafael J. Wysocki /* 669046d9ce6SRafael J. Wysocki * Per spec, only one interrupt per descriptor is allowed in 670046d9ce6SRafael J. Wysocki * _CRS, but some firmware violates this, so parse them all. 671046d9ce6SRafael J. Wysocki */ 672046d9ce6SRafael J. Wysocki irq = &ares->data.irq; 673046d9ce6SRafael J. Wysocki if (index >= irq->interrupt_count) { 6741c3f69b4SJohn Garry irqresource_disabled(res, 0); 675046d9ce6SRafael J. Wysocki return false; 676046d9ce6SRafael J. Wysocki } 677046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, irq->interrupts[index], 678046d9ce6SRafael J. Wysocki irq->triggering, irq->polarity, 6795ff81160SRaul E Rangel irq->shareable, irq->wake_capable, 6805ff81160SRaul E Rangel true); 681046d9ce6SRafael J. Wysocki break; 682046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 683046d9ce6SRafael J. Wysocki ext_irq = &ares->data.extended_irq; 684046d9ce6SRafael J. Wysocki if (index >= ext_irq->interrupt_count) { 6851c3f69b4SJohn Garry irqresource_disabled(res, 0); 686046d9ce6SRafael J. Wysocki return false; 687046d9ce6SRafael J. Wysocki } 688fa20b176SAgustin Vega-Frias if (is_gsi(ext_irq)) 689046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, ext_irq->interrupts[index], 690046d9ce6SRafael J. Wysocki ext_irq->triggering, ext_irq->polarity, 6915ff81160SRaul E Rangel ext_irq->shareable, ext_irq->wake_capable, 6925ff81160SRaul E Rangel false); 693fa20b176SAgustin Vega-Frias else 6941c3f69b4SJohn Garry irqresource_disabled(res, 0); 695046d9ce6SRafael J. Wysocki break; 696046d9ce6SRafael J. Wysocki default: 697581c19f3SJiang Liu res->flags = 0; 698046d9ce6SRafael J. Wysocki return false; 699046d9ce6SRafael J. Wysocki } 700046d9ce6SRafael J. Wysocki 701046d9ce6SRafael J. Wysocki return true; 702046d9ce6SRafael J. Wysocki } 703046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); 7048e345c99SRafael J. Wysocki 7058e345c99SRafael J. Wysocki /** 7068e345c99SRafael J. Wysocki * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). 7078e345c99SRafael J. Wysocki * @list: The head of the resource list to free. 7088e345c99SRafael J. Wysocki */ 7098e345c99SRafael J. Wysocki void acpi_dev_free_resource_list(struct list_head *list) 7108e345c99SRafael J. Wysocki { 71190e97820SJiang Liu resource_list_free(list); 7128e345c99SRafael J. Wysocki } 7138e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); 7148e345c99SRafael J. Wysocki 7158e345c99SRafael J. Wysocki struct res_proc_context { 7168e345c99SRafael J. Wysocki struct list_head *list; 7178e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *); 7188e345c99SRafael J. Wysocki void *preproc_data; 7198e345c99SRafael J. Wysocki int count; 7208e345c99SRafael J. Wysocki int error; 7218e345c99SRafael J. Wysocki }; 7228e345c99SRafael J. Wysocki 723a49170b5SJiang Liu static acpi_status acpi_dev_new_resource_entry(struct resource_win *win, 7248e345c99SRafael J. Wysocki struct res_proc_context *c) 7258e345c99SRafael J. Wysocki { 72690e97820SJiang Liu struct resource_entry *rentry; 7278e345c99SRafael J. Wysocki 72890e97820SJiang Liu rentry = resource_list_create_entry(NULL, 0); 7298e345c99SRafael J. Wysocki if (!rentry) { 7308e345c99SRafael J. Wysocki c->error = -ENOMEM; 7318e345c99SRafael J. Wysocki return AE_NO_MEMORY; 7328e345c99SRafael J. Wysocki } 73390e97820SJiang Liu *rentry->res = win->res; 73493286f47SJiang Liu rentry->offset = win->offset; 73590e97820SJiang Liu resource_list_add_tail(rentry, c->list); 7368e345c99SRafael J. Wysocki c->count++; 7378e345c99SRafael J. Wysocki return AE_OK; 7388e345c99SRafael J. Wysocki } 7398e345c99SRafael J. Wysocki 7408e345c99SRafael J. Wysocki static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, 7418e345c99SRafael J. Wysocki void *context) 7428e345c99SRafael J. Wysocki { 7438e345c99SRafael J. Wysocki struct res_proc_context *c = context; 744a49170b5SJiang Liu struct resource_win win; 745a49170b5SJiang Liu struct resource *res = &win.res; 7468e345c99SRafael J. Wysocki int i; 7478e345c99SRafael J. Wysocki 7488e345c99SRafael J. Wysocki if (c->preproc) { 7498e345c99SRafael J. Wysocki int ret; 7508e345c99SRafael J. Wysocki 7518e345c99SRafael J. Wysocki ret = c->preproc(ares, c->preproc_data); 7528e345c99SRafael J. Wysocki if (ret < 0) { 7538e345c99SRafael J. Wysocki c->error = ret; 75412fc4dadSDaniel Scally return AE_ABORT_METHOD; 7558e345c99SRafael J. Wysocki } else if (ret > 0) { 7568e345c99SRafael J. Wysocki return AE_OK; 7578e345c99SRafael J. Wysocki } 7588e345c99SRafael J. Wysocki } 7598e345c99SRafael J. Wysocki 760a49170b5SJiang Liu memset(&win, 0, sizeof(win)); 7618e345c99SRafael J. Wysocki 762a49170b5SJiang Liu if (acpi_dev_resource_memory(ares, res) 763a49170b5SJiang Liu || acpi_dev_resource_io(ares, res) 764a49170b5SJiang Liu || acpi_dev_resource_address_space(ares, &win) 765a49170b5SJiang Liu || acpi_dev_resource_ext_address_space(ares, &win)) 766a49170b5SJiang Liu return acpi_dev_new_resource_entry(&win, c); 7678e345c99SRafael J. Wysocki 768a49170b5SJiang Liu for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) { 7698e345c99SRafael J. Wysocki acpi_status status; 7708e345c99SRafael J. Wysocki 771a49170b5SJiang Liu status = acpi_dev_new_resource_entry(&win, c); 7728e345c99SRafael J. Wysocki if (ACPI_FAILURE(status)) 7738e345c99SRafael J. Wysocki return status; 7748e345c99SRafael J. Wysocki } 7758e345c99SRafael J. Wysocki 7768e345c99SRafael J. Wysocki return AE_OK; 7778e345c99SRafael J. Wysocki } 7788e345c99SRafael J. Wysocki 7794f0450afSLorenzo Pieralisi static int __acpi_dev_get_resources(struct acpi_device *adev, 7804f0450afSLorenzo Pieralisi struct list_head *list, 7814f0450afSLorenzo Pieralisi int (*preproc)(struct acpi_resource *, void *), 7824f0450afSLorenzo Pieralisi void *preproc_data, char *method) 7834f0450afSLorenzo Pieralisi { 7844f0450afSLorenzo Pieralisi struct res_proc_context c; 7854f0450afSLorenzo Pieralisi acpi_status status; 7864f0450afSLorenzo Pieralisi 7874f0450afSLorenzo Pieralisi if (!adev || !adev->handle || !list_empty(list)) 7884f0450afSLorenzo Pieralisi return -EINVAL; 7894f0450afSLorenzo Pieralisi 7904f0450afSLorenzo Pieralisi if (!acpi_has_method(adev->handle, method)) 7914f0450afSLorenzo Pieralisi return 0; 7924f0450afSLorenzo Pieralisi 7934f0450afSLorenzo Pieralisi c.list = list; 7944f0450afSLorenzo Pieralisi c.preproc = preproc; 7954f0450afSLorenzo Pieralisi c.preproc_data = preproc_data; 7964f0450afSLorenzo Pieralisi c.count = 0; 7974f0450afSLorenzo Pieralisi c.error = 0; 7984f0450afSLorenzo Pieralisi status = acpi_walk_resources(adev->handle, method, 7994f0450afSLorenzo Pieralisi acpi_dev_process_resource, &c); 8004f0450afSLorenzo Pieralisi if (ACPI_FAILURE(status)) { 8014f0450afSLorenzo Pieralisi acpi_dev_free_resource_list(list); 8024f0450afSLorenzo Pieralisi return c.error ? c.error : -EIO; 8034f0450afSLorenzo Pieralisi } 8044f0450afSLorenzo Pieralisi 8054f0450afSLorenzo Pieralisi return c.count; 8064f0450afSLorenzo Pieralisi } 8074f0450afSLorenzo Pieralisi 8088e345c99SRafael J. Wysocki /** 8098e345c99SRafael J. Wysocki * acpi_dev_get_resources - Get current resources of a device. 8108e345c99SRafael J. Wysocki * @adev: ACPI device node to get the resources for. 8118e345c99SRafael J. Wysocki * @list: Head of the resultant list of resources (must be empty). 8128e345c99SRafael J. Wysocki * @preproc: The caller's preprocessing routine. 8138e345c99SRafael J. Wysocki * @preproc_data: Pointer passed to the caller's preprocessing routine. 8148e345c99SRafael J. Wysocki * 8158e345c99SRafael J. Wysocki * Evaluate the _CRS method for the given device node and process its output by 816935ab850STom Saeger * (1) executing the @preproc() routine provided by the caller, passing the 8178e345c99SRafael J. Wysocki * resource pointer and @preproc_data to it as arguments, for each ACPI resource 8188e345c99SRafael J. Wysocki * returned and (2) converting all of the returned ACPI resources into struct 8198e345c99SRafael J. Wysocki * resource objects if possible. If the return value of @preproc() in step (1) 8208e345c99SRafael J. Wysocki * is different from 0, step (2) is not applied to the given ACPI resource and 8218e345c99SRafael J. Wysocki * if that value is negative, the whole processing is aborted and that value is 8228e345c99SRafael J. Wysocki * returned as the final error code. 8238e345c99SRafael J. Wysocki * 8248e345c99SRafael J. Wysocki * The resultant struct resource objects are put on the list pointed to by 82590e97820SJiang Liu * @list, that must be empty initially, as members of struct resource_entry 8268e345c99SRafael J. Wysocki * objects. Callers of this routine should use %acpi_dev_free_resource_list() to 8278e345c99SRafael J. Wysocki * free that list. 8288e345c99SRafael J. Wysocki * 8298e345c99SRafael J. Wysocki * The number of resources in the output list is returned on success, an error 8308e345c99SRafael J. Wysocki * code reflecting the error condition is returned otherwise. 8318e345c99SRafael J. Wysocki */ 8328e345c99SRafael J. Wysocki int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, 8338e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *), 8348e345c99SRafael J. Wysocki void *preproc_data) 8358e345c99SRafael J. Wysocki { 8364f0450afSLorenzo Pieralisi return __acpi_dev_get_resources(adev, list, preproc, preproc_data, 8374f0450afSLorenzo Pieralisi METHOD_NAME__CRS); 8388e345c99SRafael J. Wysocki } 8398e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_get_resources); 84062d1141fSJiang Liu 841c04ac679SLorenzo Pieralisi static int is_memory(struct acpi_resource *ares, void *not_used) 842c04ac679SLorenzo Pieralisi { 843c04ac679SLorenzo Pieralisi struct resource_win win; 844c04ac679SLorenzo Pieralisi struct resource *res = &win.res; 845c04ac679SLorenzo Pieralisi 846c04ac679SLorenzo Pieralisi memset(&win, 0, sizeof(win)); 847c04ac679SLorenzo Pieralisi 8486d2b5a1cSHeikki Krogerus if (acpi_dev_filter_resource_type(ares, IORESOURCE_MEM)) 8496d2b5a1cSHeikki Krogerus return 1; 8506d2b5a1cSHeikki Krogerus 851c04ac679SLorenzo Pieralisi return !(acpi_dev_resource_memory(ares, res) 852c04ac679SLorenzo Pieralisi || acpi_dev_resource_address_space(ares, &win) 853c04ac679SLorenzo Pieralisi || acpi_dev_resource_ext_address_space(ares, &win)); 854c04ac679SLorenzo Pieralisi } 855c04ac679SLorenzo Pieralisi 856c04ac679SLorenzo Pieralisi /** 857c04ac679SLorenzo Pieralisi * acpi_dev_get_dma_resources - Get current DMA resources of a device. 858c04ac679SLorenzo Pieralisi * @adev: ACPI device node to get the resources for. 859c04ac679SLorenzo Pieralisi * @list: Head of the resultant list of resources (must be empty). 860c04ac679SLorenzo Pieralisi * 861c04ac679SLorenzo Pieralisi * Evaluate the _DMA method for the given device node and process its 862c04ac679SLorenzo Pieralisi * output. 863c04ac679SLorenzo Pieralisi * 864c04ac679SLorenzo Pieralisi * The resultant struct resource objects are put on the list pointed to 865c04ac679SLorenzo Pieralisi * by @list, that must be empty initially, as members of struct 866c04ac679SLorenzo Pieralisi * resource_entry objects. Callers of this routine should use 867c04ac679SLorenzo Pieralisi * %acpi_dev_free_resource_list() to free that list. 868c04ac679SLorenzo Pieralisi * 869c04ac679SLorenzo Pieralisi * The number of resources in the output list is returned on success, 870c04ac679SLorenzo Pieralisi * an error code reflecting the error condition is returned otherwise. 871c04ac679SLorenzo Pieralisi */ 872c04ac679SLorenzo Pieralisi int acpi_dev_get_dma_resources(struct acpi_device *adev, struct list_head *list) 873c04ac679SLorenzo Pieralisi { 874c04ac679SLorenzo Pieralisi return __acpi_dev_get_resources(adev, list, is_memory, NULL, 875c04ac679SLorenzo Pieralisi METHOD_NAME__DMA); 876c04ac679SLorenzo Pieralisi } 877c04ac679SLorenzo Pieralisi EXPORT_SYMBOL_GPL(acpi_dev_get_dma_resources); 878c04ac679SLorenzo Pieralisi 87962d1141fSJiang Liu /** 8806bb057bfSHeikki Krogerus * acpi_dev_get_memory_resources - Get current memory resources of a device. 8816bb057bfSHeikki Krogerus * @adev: ACPI device node to get the resources for. 8826bb057bfSHeikki Krogerus * @list: Head of the resultant list of resources (must be empty). 8836bb057bfSHeikki Krogerus * 8846bb057bfSHeikki Krogerus * This is a helper function that locates all memory type resources of @adev 8856bb057bfSHeikki Krogerus * with acpi_dev_get_resources(). 8866bb057bfSHeikki Krogerus * 8876bb057bfSHeikki Krogerus * The number of resources in the output list is returned on success, an error 8886bb057bfSHeikki Krogerus * code reflecting the error condition is returned otherwise. 8896bb057bfSHeikki Krogerus */ 8906bb057bfSHeikki Krogerus int acpi_dev_get_memory_resources(struct acpi_device *adev, struct list_head *list) 8916bb057bfSHeikki Krogerus { 8926bb057bfSHeikki Krogerus return acpi_dev_get_resources(adev, list, is_memory, NULL); 8936bb057bfSHeikki Krogerus } 8946bb057bfSHeikki Krogerus EXPORT_SYMBOL_GPL(acpi_dev_get_memory_resources); 8956bb057bfSHeikki Krogerus 8966bb057bfSHeikki Krogerus /** 89762d1141fSJiang Liu * acpi_dev_filter_resource_type - Filter ACPI resource according to resource 89862d1141fSJiang Liu * types 89962d1141fSJiang Liu * @ares: Input ACPI resource object. 90062d1141fSJiang Liu * @types: Valid resource types of IORESOURCE_XXX 90162d1141fSJiang Liu * 9022c62e849SJiang Liu * This is a helper function to support acpi_dev_get_resources(), which filters 90362d1141fSJiang Liu * ACPI resource objects according to resource types. 90462d1141fSJiang Liu */ 90562d1141fSJiang Liu int acpi_dev_filter_resource_type(struct acpi_resource *ares, 90662d1141fSJiang Liu unsigned long types) 90762d1141fSJiang Liu { 90862d1141fSJiang Liu unsigned long type = 0; 90962d1141fSJiang Liu 91062d1141fSJiang Liu switch (ares->type) { 91162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY24: 91262d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY32: 91362d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 91462d1141fSJiang Liu type = IORESOURCE_MEM; 91562d1141fSJiang Liu break; 91662d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IO: 91762d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_IO: 91862d1141fSJiang Liu type = IORESOURCE_IO; 91962d1141fSJiang Liu break; 92062d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IRQ: 92162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 92262d1141fSJiang Liu type = IORESOURCE_IRQ; 92362d1141fSJiang Liu break; 92462d1141fSJiang Liu case ACPI_RESOURCE_TYPE_DMA: 92562d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_DMA: 92662d1141fSJiang Liu type = IORESOURCE_DMA; 92762d1141fSJiang Liu break; 92862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 92962d1141fSJiang Liu type = IORESOURCE_REG; 93062d1141fSJiang Liu break; 93162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS16: 93262d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS32: 93362d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS64: 93462d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 93562d1141fSJiang Liu if (ares->data.address.resource_type == ACPI_MEMORY_RANGE) 93662d1141fSJiang Liu type = IORESOURCE_MEM; 93762d1141fSJiang Liu else if (ares->data.address.resource_type == ACPI_IO_RANGE) 93862d1141fSJiang Liu type = IORESOURCE_IO; 93962d1141fSJiang Liu else if (ares->data.address.resource_type == 94062d1141fSJiang Liu ACPI_BUS_NUMBER_RANGE) 94162d1141fSJiang Liu type = IORESOURCE_BUS; 94262d1141fSJiang Liu break; 94362d1141fSJiang Liu default: 94462d1141fSJiang Liu break; 94562d1141fSJiang Liu } 94662d1141fSJiang Liu 94762d1141fSJiang Liu return (type & types) ? 0 : 1; 94862d1141fSJiang Liu } 94962d1141fSJiang Liu EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); 95000710984SBjorn Helgaas 95100710984SBjorn Helgaas static int acpi_dev_consumes_res(struct acpi_device *adev, struct resource *res) 95200710984SBjorn Helgaas { 95300710984SBjorn Helgaas struct list_head resource_list; 95400710984SBjorn Helgaas struct resource_entry *rentry; 95500710984SBjorn Helgaas int ret, found = 0; 95600710984SBjorn Helgaas 95700710984SBjorn Helgaas INIT_LIST_HEAD(&resource_list); 95800710984SBjorn Helgaas ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 95900710984SBjorn Helgaas if (ret < 0) 96000710984SBjorn Helgaas return 0; 96100710984SBjorn Helgaas 96200710984SBjorn Helgaas list_for_each_entry(rentry, &resource_list, node) { 96300710984SBjorn Helgaas if (resource_contains(rentry->res, res)) { 96400710984SBjorn Helgaas found = 1; 96500710984SBjorn Helgaas break; 96600710984SBjorn Helgaas } 96700710984SBjorn Helgaas 96800710984SBjorn Helgaas } 96900710984SBjorn Helgaas 97000710984SBjorn Helgaas acpi_dev_free_resource_list(&resource_list); 97100710984SBjorn Helgaas return found; 97200710984SBjorn Helgaas } 97300710984SBjorn Helgaas 97400710984SBjorn Helgaas static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, 97500710984SBjorn Helgaas void *context, void **ret) 97600710984SBjorn Helgaas { 97700710984SBjorn Helgaas struct resource *res = context; 97800710984SBjorn Helgaas struct acpi_device **consumer = (struct acpi_device **) ret; 97999ece713SRafael J. Wysocki struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 98000710984SBjorn Helgaas 98199ece713SRafael J. Wysocki if (!adev) 98200710984SBjorn Helgaas return AE_OK; 98300710984SBjorn Helgaas 98400710984SBjorn Helgaas if (acpi_dev_consumes_res(adev, res)) { 98500710984SBjorn Helgaas *consumer = adev; 98600710984SBjorn Helgaas return AE_CTRL_TERMINATE; 98700710984SBjorn Helgaas } 98800710984SBjorn Helgaas 98900710984SBjorn Helgaas return AE_OK; 99000710984SBjorn Helgaas } 99100710984SBjorn Helgaas 99200710984SBjorn Helgaas /** 99300710984SBjorn Helgaas * acpi_resource_consumer - Find the ACPI device that consumes @res. 99400710984SBjorn Helgaas * @res: Resource to search for. 99500710984SBjorn Helgaas * 99600710984SBjorn Helgaas * Search the current resource settings (_CRS) of every ACPI device node 99700710984SBjorn Helgaas * for @res. If we find an ACPI device whose _CRS includes @res, return 99800710984SBjorn Helgaas * it. Otherwise, return NULL. 99900710984SBjorn Helgaas */ 100000710984SBjorn Helgaas struct acpi_device *acpi_resource_consumer(struct resource *res) 100100710984SBjorn Helgaas { 100200710984SBjorn Helgaas struct acpi_device *consumer = NULL; 100300710984SBjorn Helgaas 100400710984SBjorn Helgaas acpi_get_devices(NULL, acpi_res_consumer_cb, res, (void **) &consumer); 100500710984SBjorn Helgaas return consumer; 100600710984SBjorn Helgaas } 1007