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 }, 403892a0126SHui Wang { } 404892a0126SHui Wang }; 405892a0126SHui Wang 406e12dee37STamim Khan static const struct dmi_system_id asus_laptop[] = { 407e12dee37STamim Khan { 408e12dee37STamim Khan .ident = "Asus Vivobook K3402ZA", 409e12dee37STamim Khan .matches = { 410e12dee37STamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 411e12dee37STamim Khan DMI_MATCH(DMI_BOARD_NAME, "K3402ZA"), 412e12dee37STamim Khan }, 413e12dee37STamim Khan }, 414e12dee37STamim Khan { 415e12dee37STamim Khan .ident = "Asus Vivobook K3502ZA", 416e12dee37STamim Khan .matches = { 417e12dee37STamim Khan DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 418e12dee37STamim Khan DMI_MATCH(DMI_BOARD_NAME, "K3502ZA"), 419e12dee37STamim Khan }, 420e12dee37STamim Khan }, 4216e5cbe7cSKellen Renshaw { 4226e5cbe7cSKellen Renshaw .ident = "Asus Vivobook S5402ZA", 4236e5cbe7cSKellen Renshaw .matches = { 4246e5cbe7cSKellen Renshaw DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 4256e5cbe7cSKellen Renshaw DMI_MATCH(DMI_BOARD_NAME, "S5402ZA"), 4266e5cbe7cSKellen Renshaw }, 4276e5cbe7cSKellen Renshaw }, 428e12dee37STamim Khan { } 429e12dee37STamim Khan }; 430e12dee37STamim Khan 431bfcdf583SJiri Slaby (SUSE) static const struct dmi_system_id lenovo_82ra[] = { 432bfcdf583SJiri Slaby (SUSE) { 433bfcdf583SJiri Slaby (SUSE) .ident = "LENOVO IdeaPad Flex 5 16ALC7", 434bfcdf583SJiri Slaby (SUSE) .matches = { 435bfcdf583SJiri Slaby (SUSE) DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 436bfcdf583SJiri Slaby (SUSE) DMI_MATCH(DMI_PRODUCT_NAME, "82RA"), 437bfcdf583SJiri Slaby (SUSE) }, 438bfcdf583SJiri Slaby (SUSE) }, 439bfcdf583SJiri Slaby (SUSE) { } 440bfcdf583SJiri Slaby (SUSE) }; 441bfcdf583SJiri Slaby (SUSE) 442892a0126SHui Wang struct irq_override_cmp { 443892a0126SHui Wang const struct dmi_system_id *system; 444892a0126SHui Wang unsigned char irq; 445892a0126SHui Wang unsigned char triggering; 446892a0126SHui Wang unsigned char polarity; 447892a0126SHui Wang unsigned char shareable; 448bfcdf583SJiri Slaby (SUSE) bool override; 449892a0126SHui Wang }; 450892a0126SHui Wang 451bfcdf583SJiri Slaby (SUSE) static const struct irq_override_cmp override_table[] = { 452bfcdf583SJiri Slaby (SUSE) { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 453bfcdf583SJiri Slaby (SUSE) { asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, 454bfcdf583SJiri Slaby (SUSE) { lenovo_82ra, 6, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true }, 455bfcdf583SJiri Slaby (SUSE) { lenovo_82ra, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true }, 456892a0126SHui Wang }; 457892a0126SHui Wang 458892a0126SHui Wang static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity, 459892a0126SHui Wang u8 shareable) 460892a0126SHui Wang { 461892a0126SHui Wang int i; 462892a0126SHui Wang 463bfcdf583SJiri Slaby (SUSE) for (i = 0; i < ARRAY_SIZE(override_table); i++) { 464bfcdf583SJiri Slaby (SUSE) const struct irq_override_cmp *entry = &override_table[i]; 465bfcdf583SJiri Slaby (SUSE) 466bfcdf583SJiri Slaby (SUSE) if (dmi_check_system(entry->system) && 467bfcdf583SJiri Slaby (SUSE) entry->irq == gsi && 468bfcdf583SJiri Slaby (SUSE) entry->triggering == triggering && 469bfcdf583SJiri Slaby (SUSE) entry->polarity == polarity && 470bfcdf583SJiri Slaby (SUSE) entry->shareable == shareable) 471bfcdf583SJiri Slaby (SUSE) return entry->override; 472bfcdf583SJiri Slaby (SUSE) } 473bfcdf583SJiri Slaby (SUSE) 4749946e39fSChuanhong Guo #ifdef CONFIG_X86 4759946e39fSChuanhong Guo /* 4769946e39fSChuanhong Guo * IRQ override isn't needed on modern AMD Zen systems and 4779946e39fSChuanhong Guo * this override breaks active low IRQs on AMD Ryzen 6000 and 4789946e39fSChuanhong Guo * newer systems. Skip it. 4799946e39fSChuanhong Guo */ 4809946e39fSChuanhong Guo if (boot_cpu_has(X86_FEATURE_ZEN)) 4819946e39fSChuanhong Guo return false; 4829946e39fSChuanhong Guo #endif 4839946e39fSChuanhong Guo 484892a0126SHui Wang return true; 485892a0126SHui Wang } 486892a0126SHui Wang 487046d9ce6SRafael J. Wysocki static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, 488204ebc0aSMika Westerberg u8 triggering, u8 polarity, u8 shareable, 4895ff81160SRaul E Rangel u8 wake_capable, bool check_override) 490046d9ce6SRafael J. Wysocki { 491046d9ce6SRafael J. Wysocki int irq, p, t; 492046d9ce6SRafael J. Wysocki 493046d9ce6SRafael J. Wysocki if (!valid_IRQ(gsi)) { 4941c3f69b4SJohn Garry irqresource_disabled(res, gsi); 495046d9ce6SRafael J. Wysocki return; 496046d9ce6SRafael J. Wysocki } 497046d9ce6SRafael J. Wysocki 498046d9ce6SRafael J. Wysocki /* 49903671057SMasahiro Yamada * In IO-APIC mode, use overridden attribute. Two reasons: 500046d9ce6SRafael J. Wysocki * 1. BIOS bug in DSDT 501046d9ce6SRafael J. Wysocki * 2. BIOS uses IO-APIC mode Interrupt Source Override 502204ebc0aSMika Westerberg * 503204ebc0aSMika Westerberg * We do this only if we are dealing with IRQ() or IRQNoFlags() 504204ebc0aSMika Westerberg * resource (the legacy ISA resources). With modern ACPI 5 devices 505204ebc0aSMika Westerberg * using extended IRQ descriptors we take the IRQ configuration 506204ebc0aSMika Westerberg * from _CRS directly. 507046d9ce6SRafael J. Wysocki */ 508892a0126SHui Wang if (check_override && 509892a0126SHui Wang acpi_dev_irq_override(gsi, triggering, polarity, shareable) && 510892a0126SHui Wang !acpi_get_override_irq(gsi, &t, &p)) { 511046d9ce6SRafael J. Wysocki u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; 512046d9ce6SRafael J. Wysocki u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; 513046d9ce6SRafael J. Wysocki 514046d9ce6SRafael J. Wysocki if (triggering != trig || polarity != pol) { 515*4ef96d4dSJiri Slaby (SUSE) pr_warn("ACPI: IRQ %d override to %s%s, %s%s\n", gsi, 516*4ef96d4dSJiri Slaby (SUSE) t ? "level" : "edge", 517*4ef96d4dSJiri Slaby (SUSE) trig == triggering ? "" : "(!)", 518*4ef96d4dSJiri Slaby (SUSE) p ? "low" : "high", 519*4ef96d4dSJiri Slaby (SUSE) pol == polarity ? "" : "(!)"); 520046d9ce6SRafael J. Wysocki triggering = trig; 521046d9ce6SRafael J. Wysocki polarity = pol; 522046d9ce6SRafael J. Wysocki } 523046d9ce6SRafael J. Wysocki } 524046d9ce6SRafael J. Wysocki 5255ff81160SRaul E Rangel res->flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable); 526046d9ce6SRafael J. Wysocki irq = acpi_register_gsi(NULL, gsi, triggering, polarity); 527046d9ce6SRafael J. Wysocki if (irq >= 0) { 528046d9ce6SRafael J. Wysocki res->start = irq; 529046d9ce6SRafael J. Wysocki res->end = irq; 530046d9ce6SRafael J. Wysocki } else { 5311c3f69b4SJohn Garry irqresource_disabled(res, gsi); 532046d9ce6SRafael J. Wysocki } 533046d9ce6SRafael J. Wysocki } 534046d9ce6SRafael J. Wysocki 535046d9ce6SRafael J. Wysocki /** 536046d9ce6SRafael J. Wysocki * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information. 537046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 538046d9ce6SRafael J. Wysocki * @index: Index into the array of GSIs represented by the resource. 539046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 540046d9ce6SRafael J. Wysocki * 541046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an interrupt resource 542046d9ce6SRafael J. Wysocki * and @index does not exceed the resource's interrupt count (true is returned 543046d9ce6SRafael J. Wysocki * in that case regardless of the results of the other checks)). If that's the 544046d9ce6SRafael J. Wysocki * case, register the GSI corresponding to @index from the array of interrupts 545046d9ce6SRafael J. Wysocki * represented by the resource and populate the generic resource object pointed 546046d9ce6SRafael J. Wysocki * to by @res accordingly. If the registration of the GSI is not successful, 547046d9ce6SRafael J. Wysocki * IORESOURCE_DISABLED will be set it that object's flags. 548581c19f3SJiang Liu * 549581c19f3SJiang Liu * Return: 550581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 551581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 552581c19f3SJiang Liu * 3) true: valid assigned resource 553046d9ce6SRafael J. Wysocki */ 554046d9ce6SRafael J. Wysocki bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, 555046d9ce6SRafael J. Wysocki struct resource *res) 556046d9ce6SRafael J. Wysocki { 557046d9ce6SRafael J. Wysocki struct acpi_resource_irq *irq; 558046d9ce6SRafael J. Wysocki struct acpi_resource_extended_irq *ext_irq; 559046d9ce6SRafael J. Wysocki 560046d9ce6SRafael J. Wysocki switch (ares->type) { 561046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_IRQ: 562046d9ce6SRafael J. Wysocki /* 563046d9ce6SRafael J. Wysocki * Per spec, only one interrupt per descriptor is allowed in 564046d9ce6SRafael J. Wysocki * _CRS, but some firmware violates this, so parse them all. 565046d9ce6SRafael J. Wysocki */ 566046d9ce6SRafael J. Wysocki irq = &ares->data.irq; 567046d9ce6SRafael J. Wysocki if (index >= irq->interrupt_count) { 5681c3f69b4SJohn Garry irqresource_disabled(res, 0); 569046d9ce6SRafael J. Wysocki return false; 570046d9ce6SRafael J. Wysocki } 571046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, irq->interrupts[index], 572046d9ce6SRafael J. Wysocki irq->triggering, irq->polarity, 5735ff81160SRaul E Rangel irq->shareable, irq->wake_capable, 5745ff81160SRaul E Rangel true); 575046d9ce6SRafael J. Wysocki break; 576046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 577046d9ce6SRafael J. Wysocki ext_irq = &ares->data.extended_irq; 578046d9ce6SRafael J. Wysocki if (index >= ext_irq->interrupt_count) { 5791c3f69b4SJohn Garry irqresource_disabled(res, 0); 580046d9ce6SRafael J. Wysocki return false; 581046d9ce6SRafael J. Wysocki } 582fa20b176SAgustin Vega-Frias if (is_gsi(ext_irq)) 583046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, ext_irq->interrupts[index], 584046d9ce6SRafael J. Wysocki ext_irq->triggering, ext_irq->polarity, 5855ff81160SRaul E Rangel ext_irq->shareable, ext_irq->wake_capable, 5865ff81160SRaul E Rangel false); 587fa20b176SAgustin Vega-Frias else 5881c3f69b4SJohn Garry irqresource_disabled(res, 0); 589046d9ce6SRafael J. Wysocki break; 590046d9ce6SRafael J. Wysocki default: 591581c19f3SJiang Liu res->flags = 0; 592046d9ce6SRafael J. Wysocki return false; 593046d9ce6SRafael J. Wysocki } 594046d9ce6SRafael J. Wysocki 595046d9ce6SRafael J. Wysocki return true; 596046d9ce6SRafael J. Wysocki } 597046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); 5988e345c99SRafael J. Wysocki 5998e345c99SRafael J. Wysocki /** 6008e345c99SRafael J. Wysocki * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). 6018e345c99SRafael J. Wysocki * @list: The head of the resource list to free. 6028e345c99SRafael J. Wysocki */ 6038e345c99SRafael J. Wysocki void acpi_dev_free_resource_list(struct list_head *list) 6048e345c99SRafael J. Wysocki { 60590e97820SJiang Liu resource_list_free(list); 6068e345c99SRafael J. Wysocki } 6078e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); 6088e345c99SRafael J. Wysocki 6098e345c99SRafael J. Wysocki struct res_proc_context { 6108e345c99SRafael J. Wysocki struct list_head *list; 6118e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *); 6128e345c99SRafael J. Wysocki void *preproc_data; 6138e345c99SRafael J. Wysocki int count; 6148e345c99SRafael J. Wysocki int error; 6158e345c99SRafael J. Wysocki }; 6168e345c99SRafael J. Wysocki 617a49170b5SJiang Liu static acpi_status acpi_dev_new_resource_entry(struct resource_win *win, 6188e345c99SRafael J. Wysocki struct res_proc_context *c) 6198e345c99SRafael J. Wysocki { 62090e97820SJiang Liu struct resource_entry *rentry; 6218e345c99SRafael J. Wysocki 62290e97820SJiang Liu rentry = resource_list_create_entry(NULL, 0); 6238e345c99SRafael J. Wysocki if (!rentry) { 6248e345c99SRafael J. Wysocki c->error = -ENOMEM; 6258e345c99SRafael J. Wysocki return AE_NO_MEMORY; 6268e345c99SRafael J. Wysocki } 62790e97820SJiang Liu *rentry->res = win->res; 62893286f47SJiang Liu rentry->offset = win->offset; 62990e97820SJiang Liu resource_list_add_tail(rentry, c->list); 6308e345c99SRafael J. Wysocki c->count++; 6318e345c99SRafael J. Wysocki return AE_OK; 6328e345c99SRafael J. Wysocki } 6338e345c99SRafael J. Wysocki 6348e345c99SRafael J. Wysocki static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, 6358e345c99SRafael J. Wysocki void *context) 6368e345c99SRafael J. Wysocki { 6378e345c99SRafael J. Wysocki struct res_proc_context *c = context; 638a49170b5SJiang Liu struct resource_win win; 639a49170b5SJiang Liu struct resource *res = &win.res; 6408e345c99SRafael J. Wysocki int i; 6418e345c99SRafael J. Wysocki 6428e345c99SRafael J. Wysocki if (c->preproc) { 6438e345c99SRafael J. Wysocki int ret; 6448e345c99SRafael J. Wysocki 6458e345c99SRafael J. Wysocki ret = c->preproc(ares, c->preproc_data); 6468e345c99SRafael J. Wysocki if (ret < 0) { 6478e345c99SRafael J. Wysocki c->error = ret; 64812fc4dadSDaniel Scally return AE_ABORT_METHOD; 6498e345c99SRafael J. Wysocki } else if (ret > 0) { 6508e345c99SRafael J. Wysocki return AE_OK; 6518e345c99SRafael J. Wysocki } 6528e345c99SRafael J. Wysocki } 6538e345c99SRafael J. Wysocki 654a49170b5SJiang Liu memset(&win, 0, sizeof(win)); 6558e345c99SRafael J. Wysocki 656a49170b5SJiang Liu if (acpi_dev_resource_memory(ares, res) 657a49170b5SJiang Liu || acpi_dev_resource_io(ares, res) 658a49170b5SJiang Liu || acpi_dev_resource_address_space(ares, &win) 659a49170b5SJiang Liu || acpi_dev_resource_ext_address_space(ares, &win)) 660a49170b5SJiang Liu return acpi_dev_new_resource_entry(&win, c); 6618e345c99SRafael J. Wysocki 662a49170b5SJiang Liu for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) { 6638e345c99SRafael J. Wysocki acpi_status status; 6648e345c99SRafael J. Wysocki 665a49170b5SJiang Liu status = acpi_dev_new_resource_entry(&win, c); 6668e345c99SRafael J. Wysocki if (ACPI_FAILURE(status)) 6678e345c99SRafael J. Wysocki return status; 6688e345c99SRafael J. Wysocki } 6698e345c99SRafael J. Wysocki 6708e345c99SRafael J. Wysocki return AE_OK; 6718e345c99SRafael J. Wysocki } 6728e345c99SRafael J. Wysocki 6734f0450afSLorenzo Pieralisi static int __acpi_dev_get_resources(struct acpi_device *adev, 6744f0450afSLorenzo Pieralisi struct list_head *list, 6754f0450afSLorenzo Pieralisi int (*preproc)(struct acpi_resource *, void *), 6764f0450afSLorenzo Pieralisi void *preproc_data, char *method) 6774f0450afSLorenzo Pieralisi { 6784f0450afSLorenzo Pieralisi struct res_proc_context c; 6794f0450afSLorenzo Pieralisi acpi_status status; 6804f0450afSLorenzo Pieralisi 6814f0450afSLorenzo Pieralisi if (!adev || !adev->handle || !list_empty(list)) 6824f0450afSLorenzo Pieralisi return -EINVAL; 6834f0450afSLorenzo Pieralisi 6844f0450afSLorenzo Pieralisi if (!acpi_has_method(adev->handle, method)) 6854f0450afSLorenzo Pieralisi return 0; 6864f0450afSLorenzo Pieralisi 6874f0450afSLorenzo Pieralisi c.list = list; 6884f0450afSLorenzo Pieralisi c.preproc = preproc; 6894f0450afSLorenzo Pieralisi c.preproc_data = preproc_data; 6904f0450afSLorenzo Pieralisi c.count = 0; 6914f0450afSLorenzo Pieralisi c.error = 0; 6924f0450afSLorenzo Pieralisi status = acpi_walk_resources(adev->handle, method, 6934f0450afSLorenzo Pieralisi acpi_dev_process_resource, &c); 6944f0450afSLorenzo Pieralisi if (ACPI_FAILURE(status)) { 6954f0450afSLorenzo Pieralisi acpi_dev_free_resource_list(list); 6964f0450afSLorenzo Pieralisi return c.error ? c.error : -EIO; 6974f0450afSLorenzo Pieralisi } 6984f0450afSLorenzo Pieralisi 6994f0450afSLorenzo Pieralisi return c.count; 7004f0450afSLorenzo Pieralisi } 7014f0450afSLorenzo Pieralisi 7028e345c99SRafael J. Wysocki /** 7038e345c99SRafael J. Wysocki * acpi_dev_get_resources - Get current resources of a device. 7048e345c99SRafael J. Wysocki * @adev: ACPI device node to get the resources for. 7058e345c99SRafael J. Wysocki * @list: Head of the resultant list of resources (must be empty). 7068e345c99SRafael J. Wysocki * @preproc: The caller's preprocessing routine. 7078e345c99SRafael J. Wysocki * @preproc_data: Pointer passed to the caller's preprocessing routine. 7088e345c99SRafael J. Wysocki * 7098e345c99SRafael J. Wysocki * Evaluate the _CRS method for the given device node and process its output by 710935ab850STom Saeger * (1) executing the @preproc() routine provided by the caller, passing the 7118e345c99SRafael J. Wysocki * resource pointer and @preproc_data to it as arguments, for each ACPI resource 7128e345c99SRafael J. Wysocki * returned and (2) converting all of the returned ACPI resources into struct 7138e345c99SRafael J. Wysocki * resource objects if possible. If the return value of @preproc() in step (1) 7148e345c99SRafael J. Wysocki * is different from 0, step (2) is not applied to the given ACPI resource and 7158e345c99SRafael J. Wysocki * if that value is negative, the whole processing is aborted and that value is 7168e345c99SRafael J. Wysocki * returned as the final error code. 7178e345c99SRafael J. Wysocki * 7188e345c99SRafael J. Wysocki * The resultant struct resource objects are put on the list pointed to by 71990e97820SJiang Liu * @list, that must be empty initially, as members of struct resource_entry 7208e345c99SRafael J. Wysocki * objects. Callers of this routine should use %acpi_dev_free_resource_list() to 7218e345c99SRafael J. Wysocki * free that list. 7228e345c99SRafael J. Wysocki * 7238e345c99SRafael J. Wysocki * The number of resources in the output list is returned on success, an error 7248e345c99SRafael J. Wysocki * code reflecting the error condition is returned otherwise. 7258e345c99SRafael J. Wysocki */ 7268e345c99SRafael J. Wysocki int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, 7278e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *), 7288e345c99SRafael J. Wysocki void *preproc_data) 7298e345c99SRafael J. Wysocki { 7304f0450afSLorenzo Pieralisi return __acpi_dev_get_resources(adev, list, preproc, preproc_data, 7314f0450afSLorenzo Pieralisi METHOD_NAME__CRS); 7328e345c99SRafael J. Wysocki } 7338e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_get_resources); 73462d1141fSJiang Liu 735c04ac679SLorenzo Pieralisi static int is_memory(struct acpi_resource *ares, void *not_used) 736c04ac679SLorenzo Pieralisi { 737c04ac679SLorenzo Pieralisi struct resource_win win; 738c04ac679SLorenzo Pieralisi struct resource *res = &win.res; 739c04ac679SLorenzo Pieralisi 740c04ac679SLorenzo Pieralisi memset(&win, 0, sizeof(win)); 741c04ac679SLorenzo Pieralisi 7426d2b5a1cSHeikki Krogerus if (acpi_dev_filter_resource_type(ares, IORESOURCE_MEM)) 7436d2b5a1cSHeikki Krogerus return 1; 7446d2b5a1cSHeikki Krogerus 745c04ac679SLorenzo Pieralisi return !(acpi_dev_resource_memory(ares, res) 746c04ac679SLorenzo Pieralisi || acpi_dev_resource_address_space(ares, &win) 747c04ac679SLorenzo Pieralisi || acpi_dev_resource_ext_address_space(ares, &win)); 748c04ac679SLorenzo Pieralisi } 749c04ac679SLorenzo Pieralisi 750c04ac679SLorenzo Pieralisi /** 751c04ac679SLorenzo Pieralisi * acpi_dev_get_dma_resources - Get current DMA resources of a device. 752c04ac679SLorenzo Pieralisi * @adev: ACPI device node to get the resources for. 753c04ac679SLorenzo Pieralisi * @list: Head of the resultant list of resources (must be empty). 754c04ac679SLorenzo Pieralisi * 755c04ac679SLorenzo Pieralisi * Evaluate the _DMA method for the given device node and process its 756c04ac679SLorenzo Pieralisi * output. 757c04ac679SLorenzo Pieralisi * 758c04ac679SLorenzo Pieralisi * The resultant struct resource objects are put on the list pointed to 759c04ac679SLorenzo Pieralisi * by @list, that must be empty initially, as members of struct 760c04ac679SLorenzo Pieralisi * resource_entry objects. Callers of this routine should use 761c04ac679SLorenzo Pieralisi * %acpi_dev_free_resource_list() to free that list. 762c04ac679SLorenzo Pieralisi * 763c04ac679SLorenzo Pieralisi * The number of resources in the output list is returned on success, 764c04ac679SLorenzo Pieralisi * an error code reflecting the error condition is returned otherwise. 765c04ac679SLorenzo Pieralisi */ 766c04ac679SLorenzo Pieralisi int acpi_dev_get_dma_resources(struct acpi_device *adev, struct list_head *list) 767c04ac679SLorenzo Pieralisi { 768c04ac679SLorenzo Pieralisi return __acpi_dev_get_resources(adev, list, is_memory, NULL, 769c04ac679SLorenzo Pieralisi METHOD_NAME__DMA); 770c04ac679SLorenzo Pieralisi } 771c04ac679SLorenzo Pieralisi EXPORT_SYMBOL_GPL(acpi_dev_get_dma_resources); 772c04ac679SLorenzo Pieralisi 77362d1141fSJiang Liu /** 7746bb057bfSHeikki Krogerus * acpi_dev_get_memory_resources - Get current memory resources of a device. 7756bb057bfSHeikki Krogerus * @adev: ACPI device node to get the resources for. 7766bb057bfSHeikki Krogerus * @list: Head of the resultant list of resources (must be empty). 7776bb057bfSHeikki Krogerus * 7786bb057bfSHeikki Krogerus * This is a helper function that locates all memory type resources of @adev 7796bb057bfSHeikki Krogerus * with acpi_dev_get_resources(). 7806bb057bfSHeikki Krogerus * 7816bb057bfSHeikki Krogerus * The number of resources in the output list is returned on success, an error 7826bb057bfSHeikki Krogerus * code reflecting the error condition is returned otherwise. 7836bb057bfSHeikki Krogerus */ 7846bb057bfSHeikki Krogerus int acpi_dev_get_memory_resources(struct acpi_device *adev, struct list_head *list) 7856bb057bfSHeikki Krogerus { 7866bb057bfSHeikki Krogerus return acpi_dev_get_resources(adev, list, is_memory, NULL); 7876bb057bfSHeikki Krogerus } 7886bb057bfSHeikki Krogerus EXPORT_SYMBOL_GPL(acpi_dev_get_memory_resources); 7896bb057bfSHeikki Krogerus 7906bb057bfSHeikki Krogerus /** 79162d1141fSJiang Liu * acpi_dev_filter_resource_type - Filter ACPI resource according to resource 79262d1141fSJiang Liu * types 79362d1141fSJiang Liu * @ares: Input ACPI resource object. 79462d1141fSJiang Liu * @types: Valid resource types of IORESOURCE_XXX 79562d1141fSJiang Liu * 7962c62e849SJiang Liu * This is a helper function to support acpi_dev_get_resources(), which filters 79762d1141fSJiang Liu * ACPI resource objects according to resource types. 79862d1141fSJiang Liu */ 79962d1141fSJiang Liu int acpi_dev_filter_resource_type(struct acpi_resource *ares, 80062d1141fSJiang Liu unsigned long types) 80162d1141fSJiang Liu { 80262d1141fSJiang Liu unsigned long type = 0; 80362d1141fSJiang Liu 80462d1141fSJiang Liu switch (ares->type) { 80562d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY24: 80662d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY32: 80762d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 80862d1141fSJiang Liu type = IORESOURCE_MEM; 80962d1141fSJiang Liu break; 81062d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IO: 81162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_IO: 81262d1141fSJiang Liu type = IORESOURCE_IO; 81362d1141fSJiang Liu break; 81462d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IRQ: 81562d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 81662d1141fSJiang Liu type = IORESOURCE_IRQ; 81762d1141fSJiang Liu break; 81862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_DMA: 81962d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_DMA: 82062d1141fSJiang Liu type = IORESOURCE_DMA; 82162d1141fSJiang Liu break; 82262d1141fSJiang Liu case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 82362d1141fSJiang Liu type = IORESOURCE_REG; 82462d1141fSJiang Liu break; 82562d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS16: 82662d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS32: 82762d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS64: 82862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 82962d1141fSJiang Liu if (ares->data.address.resource_type == ACPI_MEMORY_RANGE) 83062d1141fSJiang Liu type = IORESOURCE_MEM; 83162d1141fSJiang Liu else if (ares->data.address.resource_type == ACPI_IO_RANGE) 83262d1141fSJiang Liu type = IORESOURCE_IO; 83362d1141fSJiang Liu else if (ares->data.address.resource_type == 83462d1141fSJiang Liu ACPI_BUS_NUMBER_RANGE) 83562d1141fSJiang Liu type = IORESOURCE_BUS; 83662d1141fSJiang Liu break; 83762d1141fSJiang Liu default: 83862d1141fSJiang Liu break; 83962d1141fSJiang Liu } 84062d1141fSJiang Liu 84162d1141fSJiang Liu return (type & types) ? 0 : 1; 84262d1141fSJiang Liu } 84362d1141fSJiang Liu EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); 84400710984SBjorn Helgaas 84500710984SBjorn Helgaas static int acpi_dev_consumes_res(struct acpi_device *adev, struct resource *res) 84600710984SBjorn Helgaas { 84700710984SBjorn Helgaas struct list_head resource_list; 84800710984SBjorn Helgaas struct resource_entry *rentry; 84900710984SBjorn Helgaas int ret, found = 0; 85000710984SBjorn Helgaas 85100710984SBjorn Helgaas INIT_LIST_HEAD(&resource_list); 85200710984SBjorn Helgaas ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 85300710984SBjorn Helgaas if (ret < 0) 85400710984SBjorn Helgaas return 0; 85500710984SBjorn Helgaas 85600710984SBjorn Helgaas list_for_each_entry(rentry, &resource_list, node) { 85700710984SBjorn Helgaas if (resource_contains(rentry->res, res)) { 85800710984SBjorn Helgaas found = 1; 85900710984SBjorn Helgaas break; 86000710984SBjorn Helgaas } 86100710984SBjorn Helgaas 86200710984SBjorn Helgaas } 86300710984SBjorn Helgaas 86400710984SBjorn Helgaas acpi_dev_free_resource_list(&resource_list); 86500710984SBjorn Helgaas return found; 86600710984SBjorn Helgaas } 86700710984SBjorn Helgaas 86800710984SBjorn Helgaas static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, 86900710984SBjorn Helgaas void *context, void **ret) 87000710984SBjorn Helgaas { 87100710984SBjorn Helgaas struct resource *res = context; 87200710984SBjorn Helgaas struct acpi_device **consumer = (struct acpi_device **) ret; 87399ece713SRafael J. Wysocki struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 87400710984SBjorn Helgaas 87599ece713SRafael J. Wysocki if (!adev) 87600710984SBjorn Helgaas return AE_OK; 87700710984SBjorn Helgaas 87800710984SBjorn Helgaas if (acpi_dev_consumes_res(adev, res)) { 87900710984SBjorn Helgaas *consumer = adev; 88000710984SBjorn Helgaas return AE_CTRL_TERMINATE; 88100710984SBjorn Helgaas } 88200710984SBjorn Helgaas 88300710984SBjorn Helgaas return AE_OK; 88400710984SBjorn Helgaas } 88500710984SBjorn Helgaas 88600710984SBjorn Helgaas /** 88700710984SBjorn Helgaas * acpi_resource_consumer - Find the ACPI device that consumes @res. 88800710984SBjorn Helgaas * @res: Resource to search for. 88900710984SBjorn Helgaas * 89000710984SBjorn Helgaas * Search the current resource settings (_CRS) of every ACPI device node 89100710984SBjorn Helgaas * for @res. If we find an ACPI device whose _CRS includes @res, return 89200710984SBjorn Helgaas * it. Otherwise, return NULL. 89300710984SBjorn Helgaas */ 89400710984SBjorn Helgaas struct acpi_device *acpi_resource_consumer(struct resource *res) 89500710984SBjorn Helgaas { 89600710984SBjorn Helgaas struct acpi_device *consumer = NULL; 89700710984SBjorn Helgaas 89800710984SBjorn Helgaas acpi_get_devices(NULL, acpi_res_consumer_cb, res, (void **) &consumer); 89900710984SBjorn Helgaas return consumer; 90000710984SBjorn Helgaas } 901