diff options
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 257 |
1 files changed, 209 insertions, 48 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7fcacc5ed821..c2c585366fa6 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -44,6 +44,8 @@ | |||
44 | #include <asm/uaccess.h> | 44 | #include <asm/uaccess.h> |
45 | 45 | ||
46 | #include <linux/efi.h> | 46 | #include <linux/efi.h> |
47 | #include <linux/ioport.h> | ||
48 | #include <linux/list.h> | ||
47 | 49 | ||
48 | #define _COMPONENT ACPI_OS_SERVICES | 50 | #define _COMPONENT ACPI_OS_SERVICES |
49 | ACPI_MODULE_NAME("osl"); | 51 | ACPI_MODULE_NAME("osl"); |
@@ -74,6 +76,18 @@ static void *acpi_irq_context; | |||
74 | static struct workqueue_struct *kacpid_wq; | 76 | static struct workqueue_struct *kacpid_wq; |
75 | static struct workqueue_struct *kacpi_notify_wq; | 77 | static struct workqueue_struct *kacpi_notify_wq; |
76 | 78 | ||
79 | struct acpi_res_list { | ||
80 | resource_size_t start; | ||
81 | resource_size_t end; | ||
82 | acpi_adr_space_type resource_type; /* IO port, System memory, ...*/ | ||
83 | char name[5]; /* only can have a length of 4 chars, make use of this | ||
84 | one instead of res->name, no need to kalloc then */ | ||
85 | struct list_head resource_list; | ||
86 | }; | ||
87 | |||
88 | static LIST_HEAD(resource_list_head); | ||
89 | static DEFINE_SPINLOCK(acpi_res_lock); | ||
90 | |||
77 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ | 91 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ |
78 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; | 92 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; |
79 | 93 | ||
@@ -120,7 +134,7 @@ static char osi_additional_string[OSI_STRING_LENGTH_MAX]; | |||
120 | */ | 134 | */ |
121 | #define OSI_LINUX_ENABLE 0 | 135 | #define OSI_LINUX_ENABLE 0 |
122 | 136 | ||
123 | struct osi_linux { | 137 | static struct osi_linux { |
124 | unsigned int enable:1; | 138 | unsigned int enable:1; |
125 | unsigned int dmi:1; | 139 | unsigned int dmi:1; |
126 | unsigned int cmdline:1; | 140 | unsigned int cmdline:1; |
@@ -248,11 +262,16 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) | |||
248 | "System description tables not found\n"); | 262 | "System description tables not found\n"); |
249 | return 0; | 263 | return 0; |
250 | } | 264 | } |
251 | } else | 265 | } else { |
252 | return acpi_find_rsdp(); | 266 | acpi_physical_address pa = 0; |
267 | |||
268 | acpi_find_root_pointer(&pa); | ||
269 | return pa; | ||
270 | } | ||
253 | } | 271 | } |
254 | 272 | ||
255 | void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | 273 | void __iomem *__init_refok |
274 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | ||
256 | { | 275 | { |
257 | if (phys > ULONG_MAX) { | 276 | if (phys > ULONG_MAX) { |
258 | printk(KERN_ERR PREFIX "Cannot map memory that high\n"); | 277 | printk(KERN_ERR PREFIX "Cannot map memory that high\n"); |
@@ -330,7 +349,15 @@ acpi_os_table_override(struct acpi_table_header * existing_table, | |||
330 | 349 | ||
331 | static irqreturn_t acpi_irq(int irq, void *dev_id) | 350 | static irqreturn_t acpi_irq(int irq, void *dev_id) |
332 | { | 351 | { |
333 | return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE; | 352 | u32 handled; |
353 | |||
354 | handled = (*acpi_irq_handler) (acpi_irq_context); | ||
355 | |||
356 | if (handled) { | ||
357 | acpi_irq_handled++; | ||
358 | return IRQ_HANDLED; | ||
359 | } else | ||
360 | return IRQ_NONE; | ||
334 | } | 361 | } |
335 | 362 | ||
336 | acpi_status | 363 | acpi_status |
@@ -339,6 +366,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, | |||
339 | { | 366 | { |
340 | unsigned int irq; | 367 | unsigned int irq; |
341 | 368 | ||
369 | acpi_irq_stats_init(); | ||
370 | |||
342 | /* | 371 | /* |
343 | * Ignore the GSI from the core, and use the value in our copy of the | 372 | * Ignore the GSI from the core, and use the value in our copy of the |
344 | * FADT. It may not be the same if an interrupt source override exists | 373 | * FADT. It may not be the same if an interrupt source override exists |
@@ -653,25 +682,6 @@ static void acpi_os_execute_deferred(struct work_struct *work) | |||
653 | dpc->function(dpc->context); | 682 | dpc->function(dpc->context); |
654 | kfree(dpc); | 683 | kfree(dpc); |
655 | 684 | ||
656 | /* Yield cpu to notify thread */ | ||
657 | cond_resched(); | ||
658 | |||
659 | return; | ||
660 | } | ||
661 | |||
662 | static void acpi_os_execute_notify(struct work_struct *work) | ||
663 | { | ||
664 | struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); | ||
665 | |||
666 | if (!dpc) { | ||
667 | printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); | ||
668 | return; | ||
669 | } | ||
670 | |||
671 | dpc->function(dpc->context); | ||
672 | |||
673 | kfree(dpc); | ||
674 | |||
675 | return; | 685 | return; |
676 | } | 686 | } |
677 | 687 | ||
@@ -695,7 +705,7 @@ acpi_status acpi_os_execute(acpi_execute_type type, | |||
695 | { | 705 | { |
696 | acpi_status status = AE_OK; | 706 | acpi_status status = AE_OK; |
697 | struct acpi_os_dpc *dpc; | 707 | struct acpi_os_dpc *dpc; |
698 | 708 | struct workqueue_struct *queue; | |
699 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, | 709 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, |
700 | "Scheduling function [%p(%p)] for deferred execution.\n", | 710 | "Scheduling function [%p(%p)] for deferred execution.\n", |
701 | function, context)); | 711 | function, context)); |
@@ -719,20 +729,13 @@ acpi_status acpi_os_execute(acpi_execute_type type, | |||
719 | dpc->function = function; | 729 | dpc->function = function; |
720 | dpc->context = context; | 730 | dpc->context = context; |
721 | 731 | ||
722 | if (type == OSL_NOTIFY_HANDLER) { | 732 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); |
723 | INIT_WORK(&dpc->work, acpi_os_execute_notify); | 733 | queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq; |
724 | if (!queue_work(kacpi_notify_wq, &dpc->work)) { | 734 | if (!queue_work(queue, &dpc->work)) { |
725 | status = AE_ERROR; | 735 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
726 | kfree(dpc); | 736 | "Call to queue_work() failed.\n")); |
727 | } | 737 | status = AE_ERROR; |
728 | } else { | 738 | kfree(dpc); |
729 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); | ||
730 | if (!queue_work(kacpid_wq, &dpc->work)) { | ||
731 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
732 | "Call to queue_work() failed.\n")); | ||
733 | status = AE_ERROR; | ||
734 | kfree(dpc); | ||
735 | } | ||
736 | } | 739 | } |
737 | return_ACPI_STATUS(status); | 740 | return_ACPI_STATUS(status); |
738 | } | 741 | } |
@@ -1084,6 +1087,128 @@ static int __init acpi_wake_gpes_always_on_setup(char *str) | |||
1084 | 1087 | ||
1085 | __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); | 1088 | __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); |
1086 | 1089 | ||
1090 | /* Check of resource interference between native drivers and ACPI | ||
1091 | * OperationRegions (SystemIO and System Memory only). | ||
1092 | * IO ports and memory declared in ACPI might be used by the ACPI subsystem | ||
1093 | * in arbitrary AML code and can interfere with legacy drivers. | ||
1094 | * acpi_enforce_resources= can be set to: | ||
1095 | * | ||
1096 | * - strict (2) | ||
1097 | * -> further driver trying to access the resources will not load | ||
1098 | * - lax (default) (1) | ||
1099 | * -> further driver trying to access the resources will load, but you | ||
1100 | * get a system message that something might go wrong... | ||
1101 | * | ||
1102 | * - no (0) | ||
1103 | * -> ACPI Operation Region resources will not be registered | ||
1104 | * | ||
1105 | */ | ||
1106 | #define ENFORCE_RESOURCES_STRICT 2 | ||
1107 | #define ENFORCE_RESOURCES_LAX 1 | ||
1108 | #define ENFORCE_RESOURCES_NO 0 | ||
1109 | |||
1110 | static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_LAX; | ||
1111 | |||
1112 | static int __init acpi_enforce_resources_setup(char *str) | ||
1113 | { | ||
1114 | if (str == NULL || *str == '\0') | ||
1115 | return 0; | ||
1116 | |||
1117 | if (!strcmp("strict", str)) | ||
1118 | acpi_enforce_resources = ENFORCE_RESOURCES_STRICT; | ||
1119 | else if (!strcmp("lax", str)) | ||
1120 | acpi_enforce_resources = ENFORCE_RESOURCES_LAX; | ||
1121 | else if (!strcmp("no", str)) | ||
1122 | acpi_enforce_resources = ENFORCE_RESOURCES_NO; | ||
1123 | |||
1124 | return 1; | ||
1125 | } | ||
1126 | |||
1127 | __setup("acpi_enforce_resources=", acpi_enforce_resources_setup); | ||
1128 | |||
1129 | /* Check for resource conflicts between ACPI OperationRegions and native | ||
1130 | * drivers */ | ||
1131 | int acpi_check_resource_conflict(struct resource *res) | ||
1132 | { | ||
1133 | struct acpi_res_list *res_list_elem; | ||
1134 | int ioport; | ||
1135 | int clash = 0; | ||
1136 | |||
1137 | if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) | ||
1138 | return 0; | ||
1139 | if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM)) | ||
1140 | return 0; | ||
1141 | |||
1142 | ioport = res->flags & IORESOURCE_IO; | ||
1143 | |||
1144 | spin_lock(&acpi_res_lock); | ||
1145 | list_for_each_entry(res_list_elem, &resource_list_head, | ||
1146 | resource_list) { | ||
1147 | if (ioport && (res_list_elem->resource_type | ||
1148 | != ACPI_ADR_SPACE_SYSTEM_IO)) | ||
1149 | continue; | ||
1150 | if (!ioport && (res_list_elem->resource_type | ||
1151 | != ACPI_ADR_SPACE_SYSTEM_MEMORY)) | ||
1152 | continue; | ||
1153 | |||
1154 | if (res->end < res_list_elem->start | ||
1155 | || res_list_elem->end < res->start) | ||
1156 | continue; | ||
1157 | clash = 1; | ||
1158 | break; | ||
1159 | } | ||
1160 | spin_unlock(&acpi_res_lock); | ||
1161 | |||
1162 | if (clash) { | ||
1163 | if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) { | ||
1164 | printk(KERN_INFO "%sACPI: %s resource %s [0x%llx-0x%llx]" | ||
1165 | " conflicts with ACPI region %s" | ||
1166 | " [0x%llx-0x%llx]\n", | ||
1167 | acpi_enforce_resources == ENFORCE_RESOURCES_LAX | ||
1168 | ? KERN_WARNING : KERN_ERR, | ||
1169 | ioport ? "I/O" : "Memory", res->name, | ||
1170 | (long long) res->start, (long long) res->end, | ||
1171 | res_list_elem->name, | ||
1172 | (long long) res_list_elem->start, | ||
1173 | (long long) res_list_elem->end); | ||
1174 | printk(KERN_INFO "ACPI: Device needs an ACPI driver\n"); | ||
1175 | } | ||
1176 | if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT) | ||
1177 | return -EBUSY; | ||
1178 | } | ||
1179 | return 0; | ||
1180 | } | ||
1181 | EXPORT_SYMBOL(acpi_check_resource_conflict); | ||
1182 | |||
1183 | int acpi_check_region(resource_size_t start, resource_size_t n, | ||
1184 | const char *name) | ||
1185 | { | ||
1186 | struct resource res = { | ||
1187 | .start = start, | ||
1188 | .end = start + n - 1, | ||
1189 | .name = name, | ||
1190 | .flags = IORESOURCE_IO, | ||
1191 | }; | ||
1192 | |||
1193 | return acpi_check_resource_conflict(&res); | ||
1194 | } | ||
1195 | EXPORT_SYMBOL(acpi_check_region); | ||
1196 | |||
1197 | int acpi_check_mem_region(resource_size_t start, resource_size_t n, | ||
1198 | const char *name) | ||
1199 | { | ||
1200 | struct resource res = { | ||
1201 | .start = start, | ||
1202 | .end = start + n - 1, | ||
1203 | .name = name, | ||
1204 | .flags = IORESOURCE_MEM, | ||
1205 | }; | ||
1206 | |||
1207 | return acpi_check_resource_conflict(&res); | ||
1208 | |||
1209 | } | ||
1210 | EXPORT_SYMBOL(acpi_check_mem_region); | ||
1211 | |||
1087 | /* | 1212 | /* |
1088 | * Acquire a spinlock. | 1213 | * Acquire a spinlock. |
1089 | * | 1214 | * |
@@ -1195,24 +1320,24 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) | |||
1195 | * | 1320 | * |
1196 | * Returns 0 on success | 1321 | * Returns 0 on success |
1197 | */ | 1322 | */ |
1198 | int acpi_dmi_dump(void) | 1323 | static int acpi_dmi_dump(void) |
1199 | { | 1324 | { |
1200 | 1325 | ||
1201 | if (!dmi_available) | 1326 | if (!dmi_available) |
1202 | return -1; | 1327 | return -1; |
1203 | 1328 | ||
1204 | printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", | 1329 | printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", |
1205 | dmi_get_slot(DMI_SYS_VENDOR)); | 1330 | dmi_get_system_info(DMI_SYS_VENDOR)); |
1206 | printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", | 1331 | printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", |
1207 | dmi_get_slot(DMI_PRODUCT_NAME)); | 1332 | dmi_get_system_info(DMI_PRODUCT_NAME)); |
1208 | printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", | 1333 | printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", |
1209 | dmi_get_slot(DMI_PRODUCT_VERSION)); | 1334 | dmi_get_system_info(DMI_PRODUCT_VERSION)); |
1210 | printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", | 1335 | printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", |
1211 | dmi_get_slot(DMI_BOARD_NAME)); | 1336 | dmi_get_system_info(DMI_BOARD_NAME)); |
1212 | printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", | 1337 | printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", |
1213 | dmi_get_slot(DMI_BIOS_VENDOR)); | 1338 | dmi_get_system_info(DMI_BIOS_VENDOR)); |
1214 | printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", | 1339 | printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", |
1215 | dmi_get_slot(DMI_BIOS_DATE)); | 1340 | dmi_get_system_info(DMI_BIOS_DATE)); |
1216 | 1341 | ||
1217 | return 0; | 1342 | return 0; |
1218 | } | 1343 | } |
@@ -1285,10 +1410,46 @@ acpi_status | |||
1285 | acpi_os_validate_address ( | 1410 | acpi_os_validate_address ( |
1286 | u8 space_id, | 1411 | u8 space_id, |
1287 | acpi_physical_address address, | 1412 | acpi_physical_address address, |
1288 | acpi_size length) | 1413 | acpi_size length, |
1414 | char *name) | ||
1289 | { | 1415 | { |
1416 | struct acpi_res_list *res; | ||
1417 | if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) | ||
1418 | return AE_OK; | ||
1290 | 1419 | ||
1291 | return AE_OK; | 1420 | switch (space_id) { |
1421 | case ACPI_ADR_SPACE_SYSTEM_IO: | ||
1422 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||
1423 | /* Only interference checks against SystemIO and SytemMemory | ||
1424 | are needed */ | ||
1425 | res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL); | ||
1426 | if (!res) | ||
1427 | return AE_OK; | ||
1428 | /* ACPI names are fixed to 4 bytes, still better use strlcpy */ | ||
1429 | strlcpy(res->name, name, 5); | ||
1430 | res->start = address; | ||
1431 | res->end = address + length - 1; | ||
1432 | res->resource_type = space_id; | ||
1433 | spin_lock(&acpi_res_lock); | ||
1434 | list_add(&res->resource_list, &resource_list_head); | ||
1435 | spin_unlock(&acpi_res_lock); | ||
1436 | pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, " | ||
1437 | "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO) | ||
1438 | ? "SystemIO" : "System Memory", | ||
1439 | (unsigned long long)res->start, | ||
1440 | (unsigned long long)res->end, | ||
1441 | res->name); | ||
1442 | break; | ||
1443 | case ACPI_ADR_SPACE_PCI_CONFIG: | ||
1444 | case ACPI_ADR_SPACE_EC: | ||
1445 | case ACPI_ADR_SPACE_SMBUS: | ||
1446 | case ACPI_ADR_SPACE_CMOS: | ||
1447 | case ACPI_ADR_SPACE_PCI_BAR_TARGET: | ||
1448 | case ACPI_ADR_SPACE_DATA_TABLE: | ||
1449 | case ACPI_ADR_SPACE_FIXED_HARDWARE: | ||
1450 | break; | ||
1451 | } | ||
1452 | return AE_OK; | ||
1292 | } | 1453 | } |
1293 | 1454 | ||
1294 | #endif | 1455 | #endif |