1174 lines · 32.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * drivers/acpi/resource.c - ACPI device resources interpretation. * * Copyright (C) 2012, Intel Corp. * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/acpi.h>#include <linux/device.h>#include <linux/export.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/irq.h>#include <linux/dmi.h>#include <linux/string_choices.h> #ifdef CONFIG_X86#define valid_IRQ(i) (((i) != 0) && ((i) != 2))static inline bool acpi_iospace_resource_valid(struct resource *res){	/* On X86 IO space is limited to the [0 - 64K] IO port range */	return res->end < 0x10003;}#else#define valid_IRQ(i) (true)/* * ACPI IO descriptors on arches other than X86 contain MMIO CPU physical * addresses mapping IO space in CPU physical address space, IO space * resources can be placed anywhere in the 64-bit physical address space. */static inline boolacpi_iospace_resource_valid(struct resource *res) { return true; }#endif #if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI)static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq){	return ext_irq->resource_source.string_length == 0 &&	       ext_irq->producer_consumer == ACPI_CONSUMER;}#elsestatic inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq){	return true;}#endif static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io){	u64 reslen = end - start + 1; 	/*	 * CHECKME: len might be required to check versus a minimum	 * length as well. 1 for io is fine, but for memory it does	 * not make any sense at all.	 * Note: some BIOSes report incorrect length for ACPI address space	 * descriptor, so remove check of 'reslen == len' to avoid regression.	 */	if (len && reslen && start <= end)		return true; 	pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n",		io ? "io" : "mem", start, end, len); 	return false;} static void acpi_dev_memresource_flags(struct resource *res, u64 len,				       u8 write_protect){	res->flags = IORESOURCE_MEM; 	if (!acpi_dev_resource_len_valid(res->start, res->end, len, false))		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 	if (write_protect == ACPI_READ_WRITE_MEMORY)		res->flags |= IORESOURCE_MEM_WRITEABLE;} static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len,				     u8 write_protect){	res->start = start;	res->end = start + len - 1;	acpi_dev_memresource_flags(res, len, write_protect);} /** * acpi_dev_resource_memory - Extract ACPI memory resource information. * @ares: Input ACPI resource object. * @res: Output generic resource object. * * Check if the given ACPI resource object represents a memory resource and * if that's the case, use the information in it to populate the generic * resource object pointed to by @res. * * Return: * 1) false with res->flags setting to zero: not the expected resource type * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource * 3) true: valid assigned resource */bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res){	struct acpi_resource_memory24 *memory24;	struct acpi_resource_memory32 *memory32;	struct acpi_resource_fixed_memory32 *fixed_memory32; 	switch (ares->type) {	case ACPI_RESOURCE_TYPE_MEMORY24:		memory24 = &ares->data.memory24;		acpi_dev_get_memresource(res, memory24->minimum << 8,					 memory24->address_length << 8,					 memory24->write_protect);		break;	case ACPI_RESOURCE_TYPE_MEMORY32:		memory32 = &ares->data.memory32;		acpi_dev_get_memresource(res, memory32->minimum,					 memory32->address_length,					 memory32->write_protect);		break;	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:		fixed_memory32 = &ares->data.fixed_memory32;		acpi_dev_get_memresource(res, fixed_memory32->address,					 fixed_memory32->address_length,					 fixed_memory32->write_protect);		break;	default:		res->flags = 0;		return false;	} 	return !(res->flags & IORESOURCE_DISABLED);}EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); static void acpi_dev_ioresource_flags(struct resource *res, u64 len,				      u8 io_decode, u8 translation_type){	res->flags = IORESOURCE_IO; 	if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 	if (!acpi_iospace_resource_valid(res))		res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; 	if (io_decode == ACPI_DECODE_16)		res->flags |= IORESOURCE_IO_16BIT_ADDR;	if (translation_type == ACPI_SPARSE_TRANSLATION)		res->flags |= IORESOURCE_IO_SPARSE;} static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len,				    u8 io_decode){	res->start = start;	res->end = start + len - 1;	acpi_dev_ioresource_flags(res, len, io_decode, 0);} /** * acpi_dev_resource_io - Extract ACPI I/O resource information. * @ares: Input ACPI resource object. * @res: Output generic resource object. * * Check if the given ACPI resource object represents an I/O resource and * if that's the case, use the information in it to populate the generic * resource object pointed to by @res. * * Return: * 1) false with res->flags setting to zero: not the expected resource type * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource * 3) true: valid assigned resource */bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res){	struct acpi_resource_io *io;	struct acpi_resource_fixed_io *fixed_io; 	switch (ares->type) {	case ACPI_RESOURCE_TYPE_IO:		io = &ares->data.io;		acpi_dev_get_ioresource(res, io->minimum,					io->address_length,					io->io_decode);		break;	case ACPI_RESOURCE_TYPE_FIXED_IO:		fixed_io = &ares->data.fixed_io;		acpi_dev_get_ioresource(res, fixed_io->address,					fixed_io->address_length,					ACPI_DECODE_10);		break;	default:		res->flags = 0;		return false;	} 	return !(res->flags & IORESOURCE_DISABLED);}EXPORT_SYMBOL_GPL(acpi_dev_resource_io); static bool acpi_decode_space(struct resource_win *win,			      struct acpi_resource_address *addr,			      struct acpi_address64_attribute *attr){	u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16;	bool wp = addr->info.mem.write_protect;	u64 len = attr->address_length;	u64 start, end, offset = 0;	struct resource *res = &win->res; 	/*	 * Filter out invalid descriptor according to ACPI Spec 5.0, section	 * 6.4.3.5 Address Space Resource Descriptors.	 */	if ((addr->min_address_fixed != addr->max_address_fixed && len) ||	    (addr->min_address_fixed && addr->max_address_fixed && !len))		pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n",			 addr->min_address_fixed, addr->max_address_fixed, len); 	/*	 * For bridges that translate addresses across the bridge,	 * translation_offset is the offset that must be added to the	 * address on the secondary side to obtain the address on the	 * primary side. Non-bridge devices must list 0 for all Address	 * Translation offset bits.	 */	if (addr->producer_consumer == ACPI_PRODUCER)		offset = attr->translation_offset;	else if (attr->translation_offset)		pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n",			 attr->translation_offset);	start = attr->minimum + offset;	end = attr->maximum + offset; 	win->offset = offset;	res->start = start;	res->end = end;	if (sizeof(resource_size_t) < sizeof(u64) &&	    (offset != win->offset || start != res->start || end != res->end)) {		pr_warn("acpi resource window ([%#llx-%#llx] ignored, not CPU addressable)\n",			attr->minimum, attr->maximum);		return false;	} 	switch (addr->resource_type) {	case ACPI_MEMORY_RANGE:		acpi_dev_memresource_flags(res, len, wp); 		if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY)			res->flags |= IORESOURCE_PREFETCH;		break;	case ACPI_IO_RANGE:		acpi_dev_ioresource_flags(res, len, iodec,					  addr->info.io.translation_type);		break;	case ACPI_BUS_NUMBER_RANGE:		res->flags = IORESOURCE_BUS;		break;	default:		return false;	} 	if (addr->producer_consumer == ACPI_PRODUCER)		res->flags |= IORESOURCE_WINDOW; 	return !(res->flags & IORESOURCE_DISABLED);} /** * acpi_dev_resource_address_space - Extract ACPI address space information. * @ares: Input ACPI resource object. * @win: Output generic resource object. * * Check if the given ACPI resource object represents an address space resource * and if that's the case, use the information in it to populate the generic * resource object pointed to by @win. * * Return: * 1) false with win->res.flags setting to zero: not the expected resource type * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned *    resource * 3) true: valid assigned resource */bool acpi_dev_resource_address_space(struct acpi_resource *ares,				     struct resource_win *win){	struct acpi_resource_address64 addr; 	win->res.flags = 0;	if (ACPI_FAILURE(acpi_resource_to_address64(ares, &addr)))		return false; 	return acpi_decode_space(win, (struct acpi_resource_address *)&addr,				 &addr.address);}EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space); /** * acpi_dev_resource_ext_address_space - Extract ACPI address space information. * @ares: Input ACPI resource object. * @win: Output generic resource object. * * Check if the given ACPI resource object represents an extended address space * resource and if that's the case, use the information in it to populate the * generic resource object pointed to by @win. * * Return: * 1) false with win->res.flags setting to zero: not the expected resource type * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned *    resource * 3) true: valid assigned resource */bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,					 struct resource_win *win){	struct acpi_resource_extended_address64 *ext_addr; 	win->res.flags = 0;	if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64)		return false; 	ext_addr = &ares->data.ext_address64; 	return acpi_decode_space(win, (struct acpi_resource_address *)ext_addr,				 &ext_addr->address);}EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space); /** * acpi_dev_irq_flags - Determine IRQ resource flags. * @triggering: Triggering type as provided by ACPI. * @polarity: Interrupt polarity as provided by ACPI. * @shareable: Whether or not the interrupt is shareable. * @wake_capable: Wake capability as provided by ACPI. */unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable, u8 wake_capable){	unsigned long flags; 	if (triggering == ACPI_LEVEL_SENSITIVE)		flags = polarity == ACPI_ACTIVE_LOW ?			IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL;	else		flags = polarity == ACPI_ACTIVE_LOW ?			IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE; 	if (shareable == ACPI_SHARED)		flags |= IORESOURCE_IRQ_SHAREABLE; 	if (wake_capable == ACPI_WAKE_CAPABLE)		flags |= IORESOURCE_IRQ_WAKECAPABLE; 	return flags | IORESOURCE_IRQ;}EXPORT_SYMBOL_GPL(acpi_dev_irq_flags); /** * acpi_dev_get_irq_type - Determine irq type. * @triggering: Triggering type as provided by ACPI. * @polarity: Interrupt polarity as provided by ACPI. */unsigned int acpi_dev_get_irq_type(int triggering, int polarity){	switch (polarity) {	case ACPI_ACTIVE_LOW:		return triggering == ACPI_EDGE_SENSITIVE ?		       IRQ_TYPE_EDGE_FALLING :		       IRQ_TYPE_LEVEL_LOW;	case ACPI_ACTIVE_HIGH:		return triggering == ACPI_EDGE_SENSITIVE ?		       IRQ_TYPE_EDGE_RISING :		       IRQ_TYPE_LEVEL_HIGH;	case ACPI_ACTIVE_BOTH:		if (triggering == ACPI_EDGE_SENSITIVE)			return IRQ_TYPE_EDGE_BOTH;		fallthrough;	default:		return IRQ_TYPE_NONE;	}}EXPORT_SYMBOL_GPL(acpi_dev_get_irq_type); /* * DMI matches for boards where the DSDT specifies the kbd IRQ as * level active-low and using the override changes this to rising edge, * stopping the keyboard from working. */static const struct dmi_system_id irq1_level_low_skip_override[] = {	{		/* MEDION P15651 */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),			DMI_MATCH(DMI_BOARD_NAME, "M15T"),		},	},	{		/* MEDION S17405 */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),			DMI_MATCH(DMI_BOARD_NAME, "M17T"),		},	},	{		/* MEDION S17413 */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),			DMI_MATCH(DMI_BOARD_NAME, "M1xA"),		},	},	{		/* Asus Vivobook K3402ZA */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "K3402ZA"),		},	},	{		/* Asus Vivobook K3502ZA */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "K3502ZA"),		},	},	{		/* Asus Vivobook S5402ZA */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "S5402ZA"),		},	},	{		/* Asus Vivobook S5602ZA */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "S5602ZA"),		},	},	{		/* Asus Vivobook X1404VAP */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "X1404VAP"),		},	},	{		/* Asus Vivobook X1504VAP */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "X1504VAP"),		},	},	{		/* Asus Vivobook X1704VAP */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "X1704VAP"),		},	},	{		/* Asus ExpertBook B1402C* */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "B1402C"),		},	},	{		/* Asus ExpertBook B1502C* */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "B1502C"),		},	},	{		/* Asus ExpertBook B2402 (B2402CBA / B2402FBA / B2402CVA / B2402FVA) */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "B2402"),		},	},	{		/* Asus ExpertBook B2502 (B2502CBA / B2502FBA / B2502CVA / B2502FVA) */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "B2502"),		},	},	{		/* Asus Vivobook Go E1404GA* */		.matches = {			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),			DMI_MATCH(DMI_BOARD_NAME, "E1404GA"),		},	},	{

Lines 1–500 of 1174.