1046d9ce6SRafael J. Wysocki /* 2046d9ce6SRafael J. Wysocki * drivers/acpi/resource.c - ACPI device resources interpretation. 3046d9ce6SRafael J. Wysocki * 4046d9ce6SRafael J. Wysocki * Copyright (C) 2012, Intel Corp. 5046d9ce6SRafael J. Wysocki * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 6046d9ce6SRafael J. Wysocki * 7046d9ce6SRafael J. Wysocki * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8046d9ce6SRafael J. Wysocki * 9046d9ce6SRafael J. Wysocki * This program is free software; you can redistribute it and/or modify 10046d9ce6SRafael J. Wysocki * it under the terms of the GNU General Public License version 2 as published 11046d9ce6SRafael J. Wysocki * by the Free Software Foundation. 12046d9ce6SRafael J. Wysocki * 13046d9ce6SRafael J. Wysocki * This program is distributed in the hope that it will be useful, but 14046d9ce6SRafael J. Wysocki * WITHOUT ANY WARRANTY; without even the implied warranty of 15046d9ce6SRafael J. Wysocki * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16046d9ce6SRafael J. Wysocki * General Public License for more details. 17046d9ce6SRafael J. Wysocki * 18046d9ce6SRafael J. Wysocki * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19046d9ce6SRafael J. Wysocki */ 20046d9ce6SRafael J. Wysocki 21046d9ce6SRafael J. Wysocki #include <linux/acpi.h> 22046d9ce6SRafael J. Wysocki #include <linux/device.h> 23046d9ce6SRafael J. Wysocki #include <linux/export.h> 24046d9ce6SRafael J. Wysocki #include <linux/ioport.h> 258e345c99SRafael J. Wysocki #include <linux/slab.h> 2655a93417SChristophe RICARD #include <linux/irq.h> 27046d9ce6SRafael J. Wysocki 28046d9ce6SRafael J. Wysocki #ifdef CONFIG_X86 29046d9ce6SRafael J. Wysocki #define valid_IRQ(i) (((i) != 0) && ((i) != 2)) 30*4a2e7aabSLorenzo Pieralisi static inline bool acpi_iospace_resource_valid(struct resource *res) 31*4a2e7aabSLorenzo Pieralisi { 32*4a2e7aabSLorenzo Pieralisi /* On X86 IO space is limited to the [0 - 64K] IO port range */ 33*4a2e7aabSLorenzo Pieralisi return res->end < 0x10003; 34*4a2e7aabSLorenzo Pieralisi } 35046d9ce6SRafael J. Wysocki #else 36046d9ce6SRafael J. Wysocki #define valid_IRQ(i) (true) 37*4a2e7aabSLorenzo Pieralisi /* 38*4a2e7aabSLorenzo Pieralisi * ACPI IO descriptors on arches other than X86 contain MMIO CPU physical 39*4a2e7aabSLorenzo Pieralisi * addresses mapping IO space in CPU physical address space, IO space 40*4a2e7aabSLorenzo Pieralisi * resources can be placed anywhere in the 64-bit physical address space. 41*4a2e7aabSLorenzo Pieralisi */ 42*4a2e7aabSLorenzo Pieralisi static inline bool 43*4a2e7aabSLorenzo Pieralisi acpi_iospace_resource_valid(struct resource *res) { return true; } 44046d9ce6SRafael J. Wysocki #endif 45046d9ce6SRafael J. Wysocki 46c420dbd1SThomas Gleixner static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) 47046d9ce6SRafael J. Wysocki { 48c420dbd1SThomas Gleixner u64 reslen = end - start + 1; 49046d9ce6SRafael J. Wysocki 50c420dbd1SThomas Gleixner /* 51c420dbd1SThomas Gleixner * CHECKME: len might be required to check versus a minimum 52c420dbd1SThomas Gleixner * length as well. 1 for io is fine, but for memory it does 53c420dbd1SThomas Gleixner * not make any sense at all. 54aa714d28SJiang Liu * Note: some BIOSes report incorrect length for ACPI address space 55aa714d28SJiang Liu * descriptor, so remove check of 'reslen == len' to avoid regression. 56c420dbd1SThomas Gleixner */ 57aa714d28SJiang Liu if (len && reslen && start <= end) 58c420dbd1SThomas Gleixner return true; 59c420dbd1SThomas Gleixner 606a239af2SRafael J. Wysocki pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n", 61c420dbd1SThomas Gleixner io ? "io" : "mem", start, end, len); 62c420dbd1SThomas Gleixner 63c420dbd1SThomas Gleixner return false; 64c420dbd1SThomas Gleixner } 65c420dbd1SThomas Gleixner 66c420dbd1SThomas Gleixner static void acpi_dev_memresource_flags(struct resource *res, u64 len, 6772e26b0dSThomas Gleixner u8 write_protect) 68c420dbd1SThomas Gleixner { 69c420dbd1SThomas Gleixner res->flags = IORESOURCE_MEM; 70c420dbd1SThomas Gleixner 71c420dbd1SThomas Gleixner if (!acpi_dev_resource_len_valid(res->start, res->end, len, false)) 72c78b6885SJiang Liu res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 73046d9ce6SRafael J. Wysocki 74046d9ce6SRafael J. Wysocki if (write_protect == ACPI_READ_WRITE_MEMORY) 75c420dbd1SThomas Gleixner res->flags |= IORESOURCE_MEM_WRITEABLE; 76046d9ce6SRafael J. Wysocki } 77046d9ce6SRafael J. Wysocki 78046d9ce6SRafael J. Wysocki static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, 79046d9ce6SRafael J. Wysocki u8 write_protect) 80046d9ce6SRafael J. Wysocki { 81046d9ce6SRafael J. Wysocki res->start = start; 82046d9ce6SRafael J. Wysocki res->end = start + len - 1; 8372e26b0dSThomas Gleixner acpi_dev_memresource_flags(res, len, write_protect); 84046d9ce6SRafael J. Wysocki } 85046d9ce6SRafael J. Wysocki 86046d9ce6SRafael J. Wysocki /** 87046d9ce6SRafael J. Wysocki * acpi_dev_resource_memory - Extract ACPI memory resource information. 88046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 89046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 90046d9ce6SRafael J. Wysocki * 91046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents a memory resource and 92046d9ce6SRafael J. Wysocki * if that's the case, use the information in it to populate the generic 93046d9ce6SRafael J. Wysocki * resource object pointed to by @res. 94581c19f3SJiang Liu * 95581c19f3SJiang Liu * Return: 96581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 97581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 98581c19f3SJiang Liu * 3) true: valid assigned resource 99046d9ce6SRafael J. Wysocki */ 100046d9ce6SRafael J. Wysocki bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) 101046d9ce6SRafael J. Wysocki { 102046d9ce6SRafael J. Wysocki struct acpi_resource_memory24 *memory24; 103046d9ce6SRafael J. Wysocki struct acpi_resource_memory32 *memory32; 104046d9ce6SRafael J. Wysocki struct acpi_resource_fixed_memory32 *fixed_memory32; 105046d9ce6SRafael J. Wysocki 106046d9ce6SRafael J. Wysocki switch (ares->type) { 107046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_MEMORY24: 108046d9ce6SRafael J. Wysocki memory24 = &ares->data.memory24; 1098515f936SJiang Liu acpi_dev_get_memresource(res, memory24->minimum << 8, 1108515f936SJiang Liu memory24->address_length << 8, 111046d9ce6SRafael J. Wysocki memory24->write_protect); 112046d9ce6SRafael J. Wysocki break; 113046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_MEMORY32: 114046d9ce6SRafael J. Wysocki memory32 = &ares->data.memory32; 115046d9ce6SRafael J. Wysocki acpi_dev_get_memresource(res, memory32->minimum, 116046d9ce6SRafael J. Wysocki memory32->address_length, 117046d9ce6SRafael J. Wysocki memory32->write_protect); 118046d9ce6SRafael J. Wysocki break; 119046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 120046d9ce6SRafael J. Wysocki fixed_memory32 = &ares->data.fixed_memory32; 121046d9ce6SRafael J. Wysocki acpi_dev_get_memresource(res, fixed_memory32->address, 122046d9ce6SRafael J. Wysocki fixed_memory32->address_length, 123046d9ce6SRafael J. Wysocki fixed_memory32->write_protect); 124046d9ce6SRafael J. Wysocki break; 125046d9ce6SRafael J. Wysocki default: 126581c19f3SJiang Liu res->flags = 0; 127046d9ce6SRafael J. Wysocki return false; 128046d9ce6SRafael J. Wysocki } 129c420dbd1SThomas Gleixner 130c420dbd1SThomas Gleixner return !(res->flags & IORESOURCE_DISABLED); 131046d9ce6SRafael J. Wysocki } 132046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); 133046d9ce6SRafael J. Wysocki 1341d0420f1SThomas Gleixner static void acpi_dev_ioresource_flags(struct resource *res, u64 len, 13591236eccSJiang Liu u8 io_decode, u8 translation_type) 136046d9ce6SRafael J. Wysocki { 1371d0420f1SThomas Gleixner res->flags = IORESOURCE_IO; 1381d0420f1SThomas Gleixner 1391d0420f1SThomas Gleixner if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) 140c78b6885SJiang Liu res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 1411d0420f1SThomas Gleixner 142*4a2e7aabSLorenzo Pieralisi if (!acpi_iospace_resource_valid(res)) 143c78b6885SJiang Liu res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 144046d9ce6SRafael J. Wysocki 145046d9ce6SRafael J. Wysocki if (io_decode == ACPI_DECODE_16) 1461d0420f1SThomas Gleixner res->flags |= IORESOURCE_IO_16BIT_ADDR; 14791236eccSJiang Liu if (translation_type == ACPI_SPARSE_TRANSLATION) 14891236eccSJiang Liu res->flags |= IORESOURCE_IO_SPARSE; 149046d9ce6SRafael J. Wysocki } 150046d9ce6SRafael J. Wysocki 151046d9ce6SRafael J. Wysocki static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, 152046d9ce6SRafael J. Wysocki u8 io_decode) 153046d9ce6SRafael J. Wysocki { 154046d9ce6SRafael J. Wysocki res->start = start; 1551d0420f1SThomas Gleixner res->end = start + len - 1; 15691236eccSJiang Liu acpi_dev_ioresource_flags(res, len, io_decode, 0); 157046d9ce6SRafael J. Wysocki } 158046d9ce6SRafael J. Wysocki 159046d9ce6SRafael J. Wysocki /** 160046d9ce6SRafael J. Wysocki * acpi_dev_resource_io - Extract ACPI I/O resource information. 161046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 162046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 163046d9ce6SRafael J. Wysocki * 164046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an I/O resource and 165046d9ce6SRafael J. Wysocki * if that's the case, use the information in it to populate the generic 166046d9ce6SRafael J. Wysocki * resource object pointed to by @res. 167581c19f3SJiang Liu * 168581c19f3SJiang Liu * Return: 169581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 170581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 171581c19f3SJiang Liu * 3) true: valid assigned resource 172046d9ce6SRafael J. Wysocki */ 173046d9ce6SRafael J. Wysocki bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) 174046d9ce6SRafael J. Wysocki { 175046d9ce6SRafael J. Wysocki struct acpi_resource_io *io; 176046d9ce6SRafael J. Wysocki struct acpi_resource_fixed_io *fixed_io; 177046d9ce6SRafael J. Wysocki 178046d9ce6SRafael J. Wysocki switch (ares->type) { 179046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_IO: 180046d9ce6SRafael J. Wysocki io = &ares->data.io; 181046d9ce6SRafael J. Wysocki acpi_dev_get_ioresource(res, io->minimum, 182046d9ce6SRafael J. Wysocki io->address_length, 183046d9ce6SRafael J. Wysocki io->io_decode); 184046d9ce6SRafael J. Wysocki break; 185046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_FIXED_IO: 186046d9ce6SRafael J. Wysocki fixed_io = &ares->data.fixed_io; 187046d9ce6SRafael J. Wysocki acpi_dev_get_ioresource(res, fixed_io->address, 188046d9ce6SRafael J. Wysocki fixed_io->address_length, 189046d9ce6SRafael J. Wysocki ACPI_DECODE_10); 190046d9ce6SRafael J. Wysocki break; 191046d9ce6SRafael J. Wysocki default: 192581c19f3SJiang Liu res->flags = 0; 193046d9ce6SRafael J. Wysocki return false; 194046d9ce6SRafael J. Wysocki } 1951d0420f1SThomas Gleixner 1961d0420f1SThomas Gleixner return !(res->flags & IORESOURCE_DISABLED); 197046d9ce6SRafael J. Wysocki } 198046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_io); 199046d9ce6SRafael J. Wysocki 200a49170b5SJiang Liu static bool acpi_decode_space(struct resource_win *win, 201eb76d55eSThomas Gleixner struct acpi_resource_address *addr, 202eb76d55eSThomas Gleixner struct acpi_address64_attribute *attr) 203eb76d55eSThomas Gleixner { 204eb76d55eSThomas Gleixner u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16; 205eb76d55eSThomas Gleixner bool wp = addr->info.mem.write_protect; 206eb76d55eSThomas Gleixner u64 len = attr->address_length; 2071fb01ca9SJiang Liu u64 start, end, offset = 0; 208a49170b5SJiang Liu struct resource *res = &win->res; 209eb76d55eSThomas Gleixner 210a274019fSJiang Liu /* 211a274019fSJiang Liu * Filter out invalid descriptor according to ACPI Spec 5.0, section 212a274019fSJiang Liu * 6.4.3.5 Address Space Resource Descriptors. 213a274019fSJiang Liu */ 214a274019fSJiang Liu if ((addr->min_address_fixed != addr->max_address_fixed && len) || 215a274019fSJiang Liu (addr->min_address_fixed && addr->max_address_fixed && !len)) 216a274019fSJiang Liu pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n", 217a274019fSJiang Liu addr->min_address_fixed, addr->max_address_fixed, len); 218a274019fSJiang Liu 2192ea3d266SJiang Liu /* 2202ea3d266SJiang Liu * For bridges that translate addresses across the bridge, 2212ea3d266SJiang Liu * translation_offset is the offset that must be added to the 2222ea3d266SJiang Liu * address on the secondary side to obtain the address on the 2232ea3d266SJiang Liu * primary side. Non-bridge devices must list 0 for all Address 2242ea3d266SJiang Liu * Translation offset bits. 2252ea3d266SJiang Liu */ 2261fb01ca9SJiang Liu if (addr->producer_consumer == ACPI_PRODUCER) 2271fb01ca9SJiang Liu offset = attr->translation_offset; 2281fb01ca9SJiang Liu else if (attr->translation_offset) 2292ea3d266SJiang Liu pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n", 2302ea3d266SJiang Liu attr->translation_offset); 2311fb01ca9SJiang Liu start = attr->minimum + offset; 2321fb01ca9SJiang Liu end = attr->maximum + offset; 2331fb01ca9SJiang Liu 2341fb01ca9SJiang Liu win->offset = offset; 2351fb01ca9SJiang Liu res->start = start; 2361fb01ca9SJiang Liu res->end = end; 2371fb01ca9SJiang Liu if (sizeof(resource_size_t) < sizeof(u64) && 2381fb01ca9SJiang Liu (offset != win->offset || start != res->start || end != res->end)) { 2391fb01ca9SJiang Liu pr_warn("acpi resource window ([%#llx-%#llx] ignored, not CPU addressable)\n", 2401fb01ca9SJiang Liu attr->minimum, attr->maximum); 2411fb01ca9SJiang Liu return false; 2422ea3d266SJiang Liu } 2432ea3d266SJiang Liu 244eb76d55eSThomas Gleixner switch (addr->resource_type) { 245eb76d55eSThomas Gleixner case ACPI_MEMORY_RANGE: 24672e26b0dSThomas Gleixner acpi_dev_memresource_flags(res, len, wp); 247eb76d55eSThomas Gleixner break; 248eb76d55eSThomas Gleixner case ACPI_IO_RANGE: 24991236eccSJiang Liu acpi_dev_ioresource_flags(res, len, iodec, 25091236eccSJiang Liu addr->info.io.translation_type); 251eb76d55eSThomas Gleixner break; 252eb76d55eSThomas Gleixner case ACPI_BUS_NUMBER_RANGE: 253eb76d55eSThomas Gleixner res->flags = IORESOURCE_BUS; 254eb76d55eSThomas Gleixner break; 255eb76d55eSThomas Gleixner default: 256eb76d55eSThomas Gleixner return false; 257eb76d55eSThomas Gleixner } 258eb76d55eSThomas Gleixner 25972e26b0dSThomas Gleixner if (addr->producer_consumer == ACPI_PRODUCER) 26072e26b0dSThomas Gleixner res->flags |= IORESOURCE_WINDOW; 26172e26b0dSThomas Gleixner 262fcb29bbcSThomas Gleixner if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY) 263fcb29bbcSThomas Gleixner res->flags |= IORESOURCE_PREFETCH; 264fcb29bbcSThomas Gleixner 265eb76d55eSThomas Gleixner return !(res->flags & IORESOURCE_DISABLED); 266eb76d55eSThomas Gleixner } 267eb76d55eSThomas Gleixner 268046d9ce6SRafael J. Wysocki /** 269046d9ce6SRafael J. Wysocki * acpi_dev_resource_address_space - Extract ACPI address space information. 270046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 271a49170b5SJiang Liu * @win: Output generic resource object. 272046d9ce6SRafael J. Wysocki * 273046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an address space resource 274046d9ce6SRafael J. Wysocki * and if that's the case, use the information in it to populate the generic 275a49170b5SJiang Liu * resource object pointed to by @win. 276581c19f3SJiang Liu * 277581c19f3SJiang Liu * Return: 278a49170b5SJiang Liu * 1) false with win->res.flags setting to zero: not the expected resource type 279a49170b5SJiang Liu * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned 280a49170b5SJiang Liu * resource 281581c19f3SJiang Liu * 3) true: valid assigned resource 282046d9ce6SRafael J. Wysocki */ 283046d9ce6SRafael J. Wysocki bool acpi_dev_resource_address_space(struct acpi_resource *ares, 284a49170b5SJiang Liu struct resource_win *win) 285046d9ce6SRafael J. Wysocki { 286046d9ce6SRafael J. Wysocki struct acpi_resource_address64 addr; 287046d9ce6SRafael J. Wysocki 288a49170b5SJiang Liu win->res.flags = 0; 289eb76d55eSThomas Gleixner if (ACPI_FAILURE(acpi_resource_to_address64(ares, &addr))) 2906658c739SJiang Liu return false; 291046d9ce6SRafael J. Wysocki 292a49170b5SJiang Liu return acpi_decode_space(win, (struct acpi_resource_address *)&addr, 293eb76d55eSThomas Gleixner &addr.address); 294046d9ce6SRafael J. Wysocki } 295046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space); 296046d9ce6SRafael J. Wysocki 297046d9ce6SRafael J. Wysocki /** 298046d9ce6SRafael J. Wysocki * acpi_dev_resource_ext_address_space - Extract ACPI address space information. 299046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 300a49170b5SJiang Liu * @win: Output generic resource object. 301046d9ce6SRafael J. Wysocki * 302046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an extended address space 303046d9ce6SRafael J. Wysocki * resource and if that's the case, use the information in it to populate the 304a49170b5SJiang Liu * generic resource object pointed to by @win. 305581c19f3SJiang Liu * 306581c19f3SJiang Liu * Return: 307a49170b5SJiang Liu * 1) false with win->res.flags setting to zero: not the expected resource type 308a49170b5SJiang Liu * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned 309a49170b5SJiang Liu * resource 310581c19f3SJiang Liu * 3) true: valid assigned resource 311046d9ce6SRafael J. Wysocki */ 312046d9ce6SRafael J. Wysocki bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, 313a49170b5SJiang Liu struct resource_win *win) 314046d9ce6SRafael J. Wysocki { 315046d9ce6SRafael J. Wysocki struct acpi_resource_extended_address64 *ext_addr; 316046d9ce6SRafael J. Wysocki 317a49170b5SJiang Liu win->res.flags = 0; 318046d9ce6SRafael J. Wysocki if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64) 319046d9ce6SRafael J. Wysocki return false; 320046d9ce6SRafael J. Wysocki 321046d9ce6SRafael J. Wysocki ext_addr = &ares->data.ext_address64; 322046d9ce6SRafael J. Wysocki 323a49170b5SJiang Liu return acpi_decode_space(win, (struct acpi_resource_address *)ext_addr, 324eb76d55eSThomas Gleixner &ext_addr->address); 325046d9ce6SRafael J. Wysocki } 326046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space); 327046d9ce6SRafael J. Wysocki 328046d9ce6SRafael J. Wysocki /** 329046d9ce6SRafael J. Wysocki * acpi_dev_irq_flags - Determine IRQ resource flags. 330046d9ce6SRafael J. Wysocki * @triggering: Triggering type as provided by ACPI. 331046d9ce6SRafael J. Wysocki * @polarity: Interrupt polarity as provided by ACPI. 332046d9ce6SRafael J. Wysocki * @shareable: Whether or not the interrupt is shareable. 333046d9ce6SRafael J. Wysocki */ 334046d9ce6SRafael J. Wysocki unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable) 335046d9ce6SRafael J. Wysocki { 336046d9ce6SRafael J. Wysocki unsigned long flags; 337046d9ce6SRafael J. Wysocki 338046d9ce6SRafael J. Wysocki if (triggering == ACPI_LEVEL_SENSITIVE) 339046d9ce6SRafael J. Wysocki flags = polarity == ACPI_ACTIVE_LOW ? 340046d9ce6SRafael J. Wysocki IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL; 341046d9ce6SRafael J. Wysocki else 342046d9ce6SRafael J. Wysocki flags = polarity == ACPI_ACTIVE_LOW ? 343046d9ce6SRafael J. Wysocki IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE; 344046d9ce6SRafael J. Wysocki 345046d9ce6SRafael J. Wysocki if (shareable == ACPI_SHARED) 346046d9ce6SRafael J. Wysocki flags |= IORESOURCE_IRQ_SHAREABLE; 347046d9ce6SRafael J. Wysocki 348046d9ce6SRafael J. Wysocki return flags | IORESOURCE_IRQ; 349046d9ce6SRafael J. Wysocki } 350046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_irq_flags); 351046d9ce6SRafael J. Wysocki 35255a93417SChristophe RICARD /** 35355a93417SChristophe RICARD * acpi_dev_get_irq_type - Determine irq type. 35455a93417SChristophe RICARD * @triggering: Triggering type as provided by ACPI. 35555a93417SChristophe RICARD * @polarity: Interrupt polarity as provided by ACPI. 35655a93417SChristophe RICARD */ 35755a93417SChristophe RICARD unsigned int acpi_dev_get_irq_type(int triggering, int polarity) 35855a93417SChristophe RICARD { 35955a93417SChristophe RICARD switch (polarity) { 36055a93417SChristophe RICARD case ACPI_ACTIVE_LOW: 36155a93417SChristophe RICARD return triggering == ACPI_EDGE_SENSITIVE ? 36255a93417SChristophe RICARD IRQ_TYPE_EDGE_FALLING : 36355a93417SChristophe RICARD IRQ_TYPE_LEVEL_LOW; 36455a93417SChristophe RICARD case ACPI_ACTIVE_HIGH: 36555a93417SChristophe RICARD return triggering == ACPI_EDGE_SENSITIVE ? 36655a93417SChristophe RICARD IRQ_TYPE_EDGE_RISING : 36755a93417SChristophe RICARD IRQ_TYPE_LEVEL_HIGH; 36855a93417SChristophe RICARD case ACPI_ACTIVE_BOTH: 36955a93417SChristophe RICARD if (triggering == ACPI_EDGE_SENSITIVE) 37055a93417SChristophe RICARD return IRQ_TYPE_EDGE_BOTH; 37155a93417SChristophe RICARD default: 37255a93417SChristophe RICARD return IRQ_TYPE_NONE; 37355a93417SChristophe RICARD } 37455a93417SChristophe RICARD } 37555a93417SChristophe RICARD EXPORT_SYMBOL_GPL(acpi_dev_get_irq_type); 37655a93417SChristophe RICARD 377046d9ce6SRafael J. Wysocki static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi) 378046d9ce6SRafael J. Wysocki { 379046d9ce6SRafael J. Wysocki res->start = gsi; 380046d9ce6SRafael J. Wysocki res->end = gsi; 381c78b6885SJiang Liu res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED | IORESOURCE_UNSET; 382046d9ce6SRafael J. Wysocki } 383046d9ce6SRafael J. Wysocki 384046d9ce6SRafael J. Wysocki static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, 385204ebc0aSMika Westerberg u8 triggering, u8 polarity, u8 shareable, 386204ebc0aSMika Westerberg bool legacy) 387046d9ce6SRafael J. Wysocki { 388046d9ce6SRafael J. Wysocki int irq, p, t; 389046d9ce6SRafael J. Wysocki 390046d9ce6SRafael J. Wysocki if (!valid_IRQ(gsi)) { 391046d9ce6SRafael J. Wysocki acpi_dev_irqresource_disabled(res, gsi); 392046d9ce6SRafael J. Wysocki return; 393046d9ce6SRafael J. Wysocki } 394046d9ce6SRafael J. Wysocki 395046d9ce6SRafael J. Wysocki /* 396046d9ce6SRafael J. Wysocki * In IO-APIC mode, use overrided attribute. Two reasons: 397046d9ce6SRafael J. Wysocki * 1. BIOS bug in DSDT 398046d9ce6SRafael J. Wysocki * 2. BIOS uses IO-APIC mode Interrupt Source Override 399204ebc0aSMika Westerberg * 400204ebc0aSMika Westerberg * We do this only if we are dealing with IRQ() or IRQNoFlags() 401204ebc0aSMika Westerberg * resource (the legacy ISA resources). With modern ACPI 5 devices 402204ebc0aSMika Westerberg * using extended IRQ descriptors we take the IRQ configuration 403204ebc0aSMika Westerberg * from _CRS directly. 404046d9ce6SRafael J. Wysocki */ 405204ebc0aSMika Westerberg if (legacy && !acpi_get_override_irq(gsi, &t, &p)) { 406046d9ce6SRafael J. Wysocki u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; 407046d9ce6SRafael J. Wysocki u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; 408046d9ce6SRafael J. Wysocki 409046d9ce6SRafael J. Wysocki if (triggering != trig || polarity != pol) { 410046d9ce6SRafael J. Wysocki pr_warning("ACPI: IRQ %d override to %s, %s\n", gsi, 411204ebc0aSMika Westerberg t ? "level" : "edge", p ? "low" : "high"); 412046d9ce6SRafael J. Wysocki triggering = trig; 413046d9ce6SRafael J. Wysocki polarity = pol; 414046d9ce6SRafael J. Wysocki } 415046d9ce6SRafael J. Wysocki } 416046d9ce6SRafael J. Wysocki 417046d9ce6SRafael J. Wysocki res->flags = acpi_dev_irq_flags(triggering, polarity, shareable); 418046d9ce6SRafael J. Wysocki irq = acpi_register_gsi(NULL, gsi, triggering, polarity); 419046d9ce6SRafael J. Wysocki if (irq >= 0) { 420046d9ce6SRafael J. Wysocki res->start = irq; 421046d9ce6SRafael J. Wysocki res->end = irq; 422046d9ce6SRafael J. Wysocki } else { 423046d9ce6SRafael J. Wysocki acpi_dev_irqresource_disabled(res, gsi); 424046d9ce6SRafael J. Wysocki } 425046d9ce6SRafael J. Wysocki } 426046d9ce6SRafael J. Wysocki 427046d9ce6SRafael J. Wysocki /** 428046d9ce6SRafael J. Wysocki * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information. 429046d9ce6SRafael J. Wysocki * @ares: Input ACPI resource object. 430046d9ce6SRafael J. Wysocki * @index: Index into the array of GSIs represented by the resource. 431046d9ce6SRafael J. Wysocki * @res: Output generic resource object. 432046d9ce6SRafael J. Wysocki * 433046d9ce6SRafael J. Wysocki * Check if the given ACPI resource object represents an interrupt resource 434046d9ce6SRafael J. Wysocki * and @index does not exceed the resource's interrupt count (true is returned 435046d9ce6SRafael J. Wysocki * in that case regardless of the results of the other checks)). If that's the 436046d9ce6SRafael J. Wysocki * case, register the GSI corresponding to @index from the array of interrupts 437046d9ce6SRafael J. Wysocki * represented by the resource and populate the generic resource object pointed 438046d9ce6SRafael J. Wysocki * to by @res accordingly. If the registration of the GSI is not successful, 439046d9ce6SRafael J. Wysocki * IORESOURCE_DISABLED will be set it that object's flags. 440581c19f3SJiang Liu * 441581c19f3SJiang Liu * Return: 442581c19f3SJiang Liu * 1) false with res->flags setting to zero: not the expected resource type 443581c19f3SJiang Liu * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource 444581c19f3SJiang Liu * 3) true: valid assigned resource 445046d9ce6SRafael J. Wysocki */ 446046d9ce6SRafael J. Wysocki bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, 447046d9ce6SRafael J. Wysocki struct resource *res) 448046d9ce6SRafael J. Wysocki { 449046d9ce6SRafael J. Wysocki struct acpi_resource_irq *irq; 450046d9ce6SRafael J. Wysocki struct acpi_resource_extended_irq *ext_irq; 451046d9ce6SRafael J. Wysocki 452046d9ce6SRafael J. Wysocki switch (ares->type) { 453046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_IRQ: 454046d9ce6SRafael J. Wysocki /* 455046d9ce6SRafael J. Wysocki * Per spec, only one interrupt per descriptor is allowed in 456046d9ce6SRafael J. Wysocki * _CRS, but some firmware violates this, so parse them all. 457046d9ce6SRafael J. Wysocki */ 458046d9ce6SRafael J. Wysocki irq = &ares->data.irq; 459046d9ce6SRafael J. Wysocki if (index >= irq->interrupt_count) { 460046d9ce6SRafael J. Wysocki acpi_dev_irqresource_disabled(res, 0); 461046d9ce6SRafael J. Wysocki return false; 462046d9ce6SRafael J. Wysocki } 463046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, irq->interrupts[index], 464046d9ce6SRafael J. Wysocki irq->triggering, irq->polarity, 465204ebc0aSMika Westerberg irq->sharable, true); 466046d9ce6SRafael J. Wysocki break; 467046d9ce6SRafael J. Wysocki case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 468046d9ce6SRafael J. Wysocki ext_irq = &ares->data.extended_irq; 469046d9ce6SRafael J. Wysocki if (index >= ext_irq->interrupt_count) { 470046d9ce6SRafael J. Wysocki acpi_dev_irqresource_disabled(res, 0); 471046d9ce6SRafael J. Wysocki return false; 472046d9ce6SRafael J. Wysocki } 473046d9ce6SRafael J. Wysocki acpi_dev_get_irqresource(res, ext_irq->interrupts[index], 474046d9ce6SRafael J. Wysocki ext_irq->triggering, ext_irq->polarity, 475204ebc0aSMika Westerberg ext_irq->sharable, false); 476046d9ce6SRafael J. Wysocki break; 477046d9ce6SRafael J. Wysocki default: 478581c19f3SJiang Liu res->flags = 0; 479046d9ce6SRafael J. Wysocki return false; 480046d9ce6SRafael J. Wysocki } 481046d9ce6SRafael J. Wysocki 482046d9ce6SRafael J. Wysocki return true; 483046d9ce6SRafael J. Wysocki } 484046d9ce6SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); 4858e345c99SRafael J. Wysocki 4868e345c99SRafael J. Wysocki /** 4878e345c99SRafael J. Wysocki * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). 4888e345c99SRafael J. Wysocki * @list: The head of the resource list to free. 4898e345c99SRafael J. Wysocki */ 4908e345c99SRafael J. Wysocki void acpi_dev_free_resource_list(struct list_head *list) 4918e345c99SRafael J. Wysocki { 49290e97820SJiang Liu resource_list_free(list); 4938e345c99SRafael J. Wysocki } 4948e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); 4958e345c99SRafael J. Wysocki 4968e345c99SRafael J. Wysocki struct res_proc_context { 4978e345c99SRafael J. Wysocki struct list_head *list; 4988e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *); 4998e345c99SRafael J. Wysocki void *preproc_data; 5008e345c99SRafael J. Wysocki int count; 5018e345c99SRafael J. Wysocki int error; 5028e345c99SRafael J. Wysocki }; 5038e345c99SRafael J. Wysocki 504a49170b5SJiang Liu static acpi_status acpi_dev_new_resource_entry(struct resource_win *win, 5058e345c99SRafael J. Wysocki struct res_proc_context *c) 5068e345c99SRafael J. Wysocki { 50790e97820SJiang Liu struct resource_entry *rentry; 5088e345c99SRafael J. Wysocki 50990e97820SJiang Liu rentry = resource_list_create_entry(NULL, 0); 5108e345c99SRafael J. Wysocki if (!rentry) { 5118e345c99SRafael J. Wysocki c->error = -ENOMEM; 5128e345c99SRafael J. Wysocki return AE_NO_MEMORY; 5138e345c99SRafael J. Wysocki } 51490e97820SJiang Liu *rentry->res = win->res; 51593286f47SJiang Liu rentry->offset = win->offset; 51690e97820SJiang Liu resource_list_add_tail(rentry, c->list); 5178e345c99SRafael J. Wysocki c->count++; 5188e345c99SRafael J. Wysocki return AE_OK; 5198e345c99SRafael J. Wysocki } 5208e345c99SRafael J. Wysocki 5218e345c99SRafael J. Wysocki static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, 5228e345c99SRafael J. Wysocki void *context) 5238e345c99SRafael J. Wysocki { 5248e345c99SRafael J. Wysocki struct res_proc_context *c = context; 525a49170b5SJiang Liu struct resource_win win; 526a49170b5SJiang Liu struct resource *res = &win.res; 5278e345c99SRafael J. Wysocki int i; 5288e345c99SRafael J. Wysocki 5298e345c99SRafael J. Wysocki if (c->preproc) { 5308e345c99SRafael J. Wysocki int ret; 5318e345c99SRafael J. Wysocki 5328e345c99SRafael J. Wysocki ret = c->preproc(ares, c->preproc_data); 5338e345c99SRafael J. Wysocki if (ret < 0) { 5348e345c99SRafael J. Wysocki c->error = ret; 5358a66790bSRafael J. Wysocki return AE_CTRL_TERMINATE; 5368e345c99SRafael J. Wysocki } else if (ret > 0) { 5378e345c99SRafael J. Wysocki return AE_OK; 5388e345c99SRafael J. Wysocki } 5398e345c99SRafael J. Wysocki } 5408e345c99SRafael J. Wysocki 541a49170b5SJiang Liu memset(&win, 0, sizeof(win)); 5428e345c99SRafael J. Wysocki 543a49170b5SJiang Liu if (acpi_dev_resource_memory(ares, res) 544a49170b5SJiang Liu || acpi_dev_resource_io(ares, res) 545a49170b5SJiang Liu || acpi_dev_resource_address_space(ares, &win) 546a49170b5SJiang Liu || acpi_dev_resource_ext_address_space(ares, &win)) 547a49170b5SJiang Liu return acpi_dev_new_resource_entry(&win, c); 5488e345c99SRafael J. Wysocki 549a49170b5SJiang Liu for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) { 5508e345c99SRafael J. Wysocki acpi_status status; 5518e345c99SRafael J. Wysocki 552a49170b5SJiang Liu status = acpi_dev_new_resource_entry(&win, c); 5538e345c99SRafael J. Wysocki if (ACPI_FAILURE(status)) 5548e345c99SRafael J. Wysocki return status; 5558e345c99SRafael J. Wysocki } 5568e345c99SRafael J. Wysocki 5578e345c99SRafael J. Wysocki return AE_OK; 5588e345c99SRafael J. Wysocki } 5598e345c99SRafael J. Wysocki 5608e345c99SRafael J. Wysocki /** 5618e345c99SRafael J. Wysocki * acpi_dev_get_resources - Get current resources of a device. 5628e345c99SRafael J. Wysocki * @adev: ACPI device node to get the resources for. 5638e345c99SRafael J. Wysocki * @list: Head of the resultant list of resources (must be empty). 5648e345c99SRafael J. Wysocki * @preproc: The caller's preprocessing routine. 5658e345c99SRafael J. Wysocki * @preproc_data: Pointer passed to the caller's preprocessing routine. 5668e345c99SRafael J. Wysocki * 5678e345c99SRafael J. Wysocki * Evaluate the _CRS method for the given device node and process its output by 5688e345c99SRafael J. Wysocki * (1) executing the @preproc() rountine provided by the caller, passing the 5698e345c99SRafael J. Wysocki * resource pointer and @preproc_data to it as arguments, for each ACPI resource 5708e345c99SRafael J. Wysocki * returned and (2) converting all of the returned ACPI resources into struct 5718e345c99SRafael J. Wysocki * resource objects if possible. If the return value of @preproc() in step (1) 5728e345c99SRafael J. Wysocki * is different from 0, step (2) is not applied to the given ACPI resource and 5738e345c99SRafael J. Wysocki * if that value is negative, the whole processing is aborted and that value is 5748e345c99SRafael J. Wysocki * returned as the final error code. 5758e345c99SRafael J. Wysocki * 5768e345c99SRafael J. Wysocki * The resultant struct resource objects are put on the list pointed to by 57790e97820SJiang Liu * @list, that must be empty initially, as members of struct resource_entry 5788e345c99SRafael J. Wysocki * objects. Callers of this routine should use %acpi_dev_free_resource_list() to 5798e345c99SRafael J. Wysocki * free that list. 5808e345c99SRafael J. Wysocki * 5818e345c99SRafael J. Wysocki * The number of resources in the output list is returned on success, an error 5828e345c99SRafael J. Wysocki * code reflecting the error condition is returned otherwise. 5838e345c99SRafael J. Wysocki */ 5848e345c99SRafael J. Wysocki int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, 5858e345c99SRafael J. Wysocki int (*preproc)(struct acpi_resource *, void *), 5868e345c99SRafael J. Wysocki void *preproc_data) 5878e345c99SRafael J. Wysocki { 5888e345c99SRafael J. Wysocki struct res_proc_context c; 5898e345c99SRafael J. Wysocki acpi_status status; 5908e345c99SRafael J. Wysocki 5918e345c99SRafael J. Wysocki if (!adev || !adev->handle || !list_empty(list)) 5928e345c99SRafael J. Wysocki return -EINVAL; 5938e345c99SRafael J. Wysocki 594952c63e9SJiang Liu if (!acpi_has_method(adev->handle, METHOD_NAME__CRS)) 5958e345c99SRafael J. Wysocki return 0; 5968e345c99SRafael J. Wysocki 5978e345c99SRafael J. Wysocki c.list = list; 5988e345c99SRafael J. Wysocki c.preproc = preproc; 5998e345c99SRafael J. Wysocki c.preproc_data = preproc_data; 6008e345c99SRafael J. Wysocki c.count = 0; 6018e345c99SRafael J. Wysocki c.error = 0; 6028e345c99SRafael J. Wysocki status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, 6038e345c99SRafael J. Wysocki acpi_dev_process_resource, &c); 6048e345c99SRafael J. Wysocki if (ACPI_FAILURE(status)) { 6058e345c99SRafael J. Wysocki acpi_dev_free_resource_list(list); 6068e345c99SRafael J. Wysocki return c.error ? c.error : -EIO; 6078e345c99SRafael J. Wysocki } 6088e345c99SRafael J. Wysocki 6098e345c99SRafael J. Wysocki return c.count; 6108e345c99SRafael J. Wysocki } 6118e345c99SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_dev_get_resources); 61262d1141fSJiang Liu 61362d1141fSJiang Liu /** 61462d1141fSJiang Liu * acpi_dev_filter_resource_type - Filter ACPI resource according to resource 61562d1141fSJiang Liu * types 61662d1141fSJiang Liu * @ares: Input ACPI resource object. 61762d1141fSJiang Liu * @types: Valid resource types of IORESOURCE_XXX 61862d1141fSJiang Liu * 6192c62e849SJiang Liu * This is a helper function to support acpi_dev_get_resources(), which filters 62062d1141fSJiang Liu * ACPI resource objects according to resource types. 62162d1141fSJiang Liu */ 62262d1141fSJiang Liu int acpi_dev_filter_resource_type(struct acpi_resource *ares, 62362d1141fSJiang Liu unsigned long types) 62462d1141fSJiang Liu { 62562d1141fSJiang Liu unsigned long type = 0; 62662d1141fSJiang Liu 62762d1141fSJiang Liu switch (ares->type) { 62862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY24: 62962d1141fSJiang Liu case ACPI_RESOURCE_TYPE_MEMORY32: 63062d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 63162d1141fSJiang Liu type = IORESOURCE_MEM; 63262d1141fSJiang Liu break; 63362d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IO: 63462d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_IO: 63562d1141fSJiang Liu type = IORESOURCE_IO; 63662d1141fSJiang Liu break; 63762d1141fSJiang Liu case ACPI_RESOURCE_TYPE_IRQ: 63862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 63962d1141fSJiang Liu type = IORESOURCE_IRQ; 64062d1141fSJiang Liu break; 64162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_DMA: 64262d1141fSJiang Liu case ACPI_RESOURCE_TYPE_FIXED_DMA: 64362d1141fSJiang Liu type = IORESOURCE_DMA; 64462d1141fSJiang Liu break; 64562d1141fSJiang Liu case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 64662d1141fSJiang Liu type = IORESOURCE_REG; 64762d1141fSJiang Liu break; 64862d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS16: 64962d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS32: 65062d1141fSJiang Liu case ACPI_RESOURCE_TYPE_ADDRESS64: 65162d1141fSJiang Liu case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 65262d1141fSJiang Liu if (ares->data.address.resource_type == ACPI_MEMORY_RANGE) 65362d1141fSJiang Liu type = IORESOURCE_MEM; 65462d1141fSJiang Liu else if (ares->data.address.resource_type == ACPI_IO_RANGE) 65562d1141fSJiang Liu type = IORESOURCE_IO; 65662d1141fSJiang Liu else if (ares->data.address.resource_type == 65762d1141fSJiang Liu ACPI_BUS_NUMBER_RANGE) 65862d1141fSJiang Liu type = IORESOURCE_BUS; 65962d1141fSJiang Liu break; 66062d1141fSJiang Liu default: 66162d1141fSJiang Liu break; 66262d1141fSJiang Liu } 66362d1141fSJiang Liu 66462d1141fSJiang Liu return (type & types) ? 0 : 1; 66562d1141fSJiang Liu } 66662d1141fSJiang Liu EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); 667