From cf450136bfde77c7f95065c91bffded4aa7fa731 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 31 Jul 2011 13:23:49 -0400 Subject: ACPI: ignore FADT reset-reg-sup flag we check that the address is non-zero later anyway. https://bugzilla.kernel.org/show_bug.cgi?id=11533 Signed-off-by: Len Brown --- drivers/acpi/acpica/hwxface.c | 3 +-- drivers/acpi/reboot.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 9d38eb6c0d0..fe1fb6366aa 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -74,8 +74,7 @@ acpi_status acpi_reset(void) /* Check if the reset register is supported */ - if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || - !reset_reg->address) { + if (!reset_reg->address) { return_ACPI_STATUS(AE_NOT_EXIST); } diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index a6c77e8b37b..c1d61243593 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -23,8 +23,7 @@ void acpi_reboot(void) /* Is the reset register supported? The spec says we should be * checking the bit width and bit offset, but Windows ignores * these fields */ - if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)) - return; + /* Ignore also acpi_gbl_FADT.flags.ACPI_FADT_RESET_REGISTER */ reset_value = acpi_gbl_FADT.reset_value; -- cgit v1.2.3-70-g09d2 From 3e80acd1af40fcd91a200b0416a7616b20c5d647 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 23 Feb 2012 22:40:43 +0200 Subject: ACPICA: Fix regression in FADT revision checks commit 64b3db22c04586997ab4be46dd5a5b99f8a2d390 (2.6.39), "Remove use of unreliable FADT revision field" causes regression for old P4 systems because now cst_control and other fields are not reset to 0. The effect is that acpi_processor_power_init will notice cst_control != 0 and a write to CST_CNT register is performed that should not happen. As result, the system oopses after the "No _CST, giving up" message, sometimes in acpi_ns_internalize_name, sometimes in acpi_ns_get_type, usually at random places. May be during migration to CPU 1 in acpi_processor_get_throttling. Every one of these settings help to avoid this problem: - acpi=off - processor.nocst=1 - maxcpus=1 The fix is to update acpi_gbl_FADT.header.length after the original value is used to check for old revisions. https://bugzilla.kernel.org/show_bug.cgi?id=42700 https://bugzilla.redhat.com/show_bug.cgi?id=727865 Signed-off-by: Julian Anastasov Acked-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/tbfadt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index c5d870406f4..4c9c760db4a 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -363,10 +363,6 @@ static void acpi_tb_convert_fadt(void) u32 address32; u32 i; - /* Update the local FADT table header length */ - - acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt); - /* * Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary. * Later code will always use the X 64-bit field. Also, check for an @@ -408,6 +404,10 @@ static void acpi_tb_convert_fadt(void) acpi_gbl_FADT.boot_flags = 0; } + /* Update the local FADT table header length */ + + acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt); + /* * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X" * generic address structures as necessary. Later code will always use -- cgit v1.2.3-70-g09d2 From 2815ab92ba3ab27556212cc306288dc95692824b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 6 Feb 2012 08:17:11 -0800 Subject: ACPI: Do cpufreq clamping for throttling per package v2 On Intel CPUs the processor typically uses the highest frequency set by any logical CPU. When the system overheats Linux first forces the frequency to the lowest available one to lower the temperature. However this was done only per logical CPU, which means all logical CPUs in a package would need to go through this before the frequency is actually lowered. Worse this delay actually prevents real throttling, because the real throttle code only proceeds when the lowest frequency is already reached. So when a throttle event happens force the lowest frequency for all CPUs in the package where it happened. The per CPU state is now kept per package, not per logical CPU. An alternative would be to do it per cpufreq unit, but since we want to bring down the temperature of the complete chip it's better to do it for all. In principle it may even make sense to do it for all CPUs, but I kept it on the package for now. With this change the frequency is actually lowered, which in terms also allows real throttling to proceed. I also removed an unnecessary per cpu variable initialization. v2: Fix package mapping Cc: Signed-off-by: Andi Kleen Signed-off-by: Len Brown --- drivers/acpi/processor_thermal.c | 45 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 3b599abf2b4..641b5450a0d 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -57,6 +57,27 @@ ACPI_MODULE_NAME("processor_thermal"); static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg); static unsigned int acpi_thermal_cpufreq_is_init = 0; +#define reduction_pctg(cpu) \ + per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu)) + +/* + * Emulate "per package data" using per cpu data (which should really be + * provided elsewhere) + * + * Note we can lose a CPU on cpu hotunplug, in this case we forget the state + * temporarily. Fortunately that's not a big issue here (I hope) + */ +static int phys_package_first_cpu(int cpu) +{ + int i; + int id = topology_physical_package_id(cpu); + + for_each_online_cpu(i) + if (topology_physical_package_id(i) == id) + return i; + return 0; +} + static int cpu_has_cpufreq(unsigned int cpu) { struct cpufreq_policy policy; @@ -76,7 +97,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, max_freq = ( policy->cpuinfo.max_freq * - (100 - per_cpu(cpufreq_thermal_reduction_pctg, policy->cpu) * 20) + (100 - reduction_pctg(policy->cpu) * 20) ) / 100; cpufreq_verify_within_limits(policy, 0, max_freq); @@ -102,16 +123,28 @@ static int cpufreq_get_cur_state(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return 0; - return per_cpu(cpufreq_thermal_reduction_pctg, cpu); + return reduction_pctg(cpu); } static int cpufreq_set_cur_state(unsigned int cpu, int state) { + int i; + if (!cpu_has_cpufreq(cpu)) return 0; - per_cpu(cpufreq_thermal_reduction_pctg, cpu) = state; - cpufreq_update_policy(cpu); + reduction_pctg(cpu) = state; + + /* + * Update all the CPUs in the same package because they all + * contribute to the temperature and often share the same + * frequency. + */ + for_each_online_cpu(i) { + if (topology_physical_package_id(i) == + topology_physical_package_id(cpu)) + cpufreq_update_policy(i); + } return 0; } @@ -119,10 +152,6 @@ void acpi_thermal_cpufreq_init(void) { int i; - for (i = 0; i < nr_cpu_ids; i++) - if (cpu_present(i)) - per_cpu(cpufreq_thermal_reduction_pctg, i) = 0; - i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); if (!i) -- cgit v1.2.3-70-g09d2 From d6795fe32da13bde39ea483e42799a22daa730b5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 6 Feb 2012 08:17:08 -0800 Subject: ACPI: ec: Do request_region outside WARN() WARN() is not supposed to have side effects, so move the request_regions outside. Signed-off-by: Andi Kleen Signed-off-by: Len Brown --- drivers/acpi/ec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b19a18dd994..3268dcfbaa9 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -812,10 +812,10 @@ static int acpi_ec_add(struct acpi_device *device) first_ec = ec; device->driver_data = ec; - WARN(!request_region(ec->data_addr, 1, "EC data"), - "Could not request EC data io port 0x%lx", ec->data_addr); - WARN(!request_region(ec->command_addr, 1, "EC cmd"), - "Could not request EC cmd io port 0x%lx", ec->command_addr); + ret = !!request_region(ec->data_addr, 1, "EC data"); + WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr); + ret = !!request_region(ec->command_addr, 1, "EC cmd"); + WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr); pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", ec->gpe, ec->command_addr, ec->data_addr); -- cgit v1.2.3-70-g09d2 From 6fe0d0628245fdcd6fad8b837c81e8f7ebc3364d Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 6 Feb 2012 08:17:09 -0800 Subject: ACPI: Make ACPI interrupt threaded Some ACPI interrupt actions may need to wait, and it's easiest to have a thread context for this. So turn the ACPI interrupt into a threaded interrupt. Signed-off-by: Andi Kleen Signed-off-by: Len Brown --- drivers/acpi/osl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 412a1e04a92..02367a8a60e 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -595,7 +595,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_handler = handler; acpi_irq_context = context; - if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { + if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED, "acpi", + acpi_irq)) { printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; -- cgit v1.2.3-70-g09d2 From 9bcb8118965ab4631a65ee0726e6518f75cda6c5 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 1 Feb 2012 10:26:54 -0500 Subject: ACPI: Evaluate thermal trip points before reading temperature An HP laptop (Pavilion G4-1016tx) has the following code in _TMP: Store (\_SB.PCI0.LPCB.EC0.RTMP, Local0) If (LGreaterEqual (Local0, S4TP)) { Store (One, HTS4) } S4TP is initialised at 0 and not programmed further until either _HOT or _CRT is called. If we evaluate _TMP before the trip points then HTS4 will always be set, causing the firmware to generate a message on boot complaining that the system shut down because of overheating. The simplest solution is just to reverse the checking of trip points and _TMP in thermal init. Signed-off-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/acpi/thermal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 48fbc647b17..7dbebea1ec3 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -941,13 +941,13 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) if (!tz) return -EINVAL; - /* Get temperature [_TMP] (required) */ - result = acpi_thermal_get_temperature(tz); + /* Get trip points [_CRT, _PSV, etc.] (required) */ + result = acpi_thermal_get_trip_points(tz); if (result) return result; - /* Get trip points [_CRT, _PSV, etc.] (required) */ - result = acpi_thermal_get_trip_points(tz); + /* Get temperature [_TMP] (required) */ + result = acpi_thermal_get_temperature(tz); if (result) return result; -- cgit v1.2.3-70-g09d2 From c6436f5a395d346e9f4892d7aeed4c3f99261f0f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 13 Feb 2012 17:04:43 -0700 Subject: ACPI / PM: print physical addresses consistently with other parts of kernel Print physical address info in a style consistent with the %pR style used elsewhere in the kernel. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/nvs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 7a2035fa8c7..266bc58ce0c 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -95,8 +95,8 @@ static int suspend_nvs_register(unsigned long start, unsigned long size) { struct nvs_page *entry, *next; - pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n", - start, size); + pr_info("PM: Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n", + start, start + size - 1, size); while (size > 0) { unsigned int nr_bytes; -- cgit v1.2.3-70-g09d2 From 9f324bda970c599ca35f7be89d9d1bcb96d6053c Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Mon, 19 Mar 2012 13:08:02 -0600 Subject: ACPI: Add CPU hotplug support for processor device objects acpi_processor_install_hotplug_notify() registers processor objects to receive ACPI CPU hotplug event notifications. This patch additionally registers processor device objects (ACPI0007) to receive the notifications as well. Signed-off-by: Toshi Kani Reviewed-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/processor_driver.c | 48 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 8ae05ce1850..50be27739fe 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -68,6 +68,7 @@ #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 #define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 +#define ACPI_PROCESSOR_DEVICE_HID "ACPI0007" #define ACPI_PROCESSOR_LIMIT_USER 0 #define ACPI_PROCESSOR_LIMIT_THERMAL 1 @@ -88,7 +89,7 @@ static int acpi_processor_start(struct acpi_processor *pr); static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, - {"ACPI0007", 0}, + {ACPI_PROCESSOR_DEVICE_HID, 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); @@ -741,20 +742,46 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, return; } +static acpi_status is_processor_device(acpi_handle handle) +{ + struct acpi_device_info *info; + char *hid; + acpi_status status; + + status = acpi_get_object_info(handle, &info); + if (ACPI_FAILURE(status)) + return status; + + if (info->type == ACPI_TYPE_PROCESSOR) { + kfree(info); + return AE_OK; /* found a processor object */ + } + + if (!(info->valid & ACPI_VALID_HID)) { + kfree(info); + return AE_ERROR; + } + + hid = info->hardware_id.string; + if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) { + kfree(info); + return AE_ERROR; + } + + kfree(info); + return AE_OK; /* found a processor device object */ +} + static acpi_status processor_walk_namespace_cb(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; int *action = context; - acpi_object_type type = 0; - status = acpi_get_type(handle, &type); + status = is_processor_device(handle); if (ACPI_FAILURE(status)) - return (AE_OK); - - if (type != ACPI_TYPE_PROCESSOR) - return (AE_OK); + return AE_OK; /* not a processor; continue to walk */ switch (*action) { case INSTALL_NOTIFY_HANDLER: @@ -772,7 +799,8 @@ processor_walk_namespace_cb(acpi_handle handle, break; } - return (AE_OK); + /* found a processor; skip walking underneath */ + return AE_CTRL_DEPTH; } static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) @@ -830,7 +858,7 @@ void acpi_processor_install_hotplug_notify(void) { #ifdef CONFIG_ACPI_HOTPLUG_CPU int action = INSTALL_NOTIFY_HANDLER; - acpi_walk_namespace(ACPI_TYPE_PROCESSOR, + acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, processor_walk_namespace_cb, NULL, &action, NULL); @@ -843,7 +871,7 @@ void acpi_processor_uninstall_hotplug_notify(void) { #ifdef CONFIG_ACPI_HOTPLUG_CPU int action = UNINSTALL_NOTIFY_HANDLER; - acpi_walk_namespace(ACPI_TYPE_PROCESSOR, + acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, processor_walk_namespace_cb, NULL, &action, NULL); -- cgit v1.2.3-70-g09d2 From 1a022e3f1be11730bd8747b1af96a0274bf6356e Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Tue, 13 Mar 2012 19:55:09 +0100 Subject: idle, x86: Allow off-lined CPU to enter deeper C states Currently when a CPU is off-lined it enters either MWAIT-based idle or, if MWAIT is not desired or supported, HLT-based idle (which places the processor in C1 state). This patch allows processors without MWAIT support to stay in states deeper than C1. Signed-off-by: Boris Ostrovsky Signed-off-by: Len Brown --- arch/x86/kernel/smpboot.c | 4 +++- drivers/acpi/processor_idle.c | 31 +++++++++++++++++++++++++++++++ drivers/cpuidle/cpuidle.c | 28 ++++++++++++++++++++++++++++ include/linux/cpuidle.h | 5 +++++ 4 files changed, 67 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 66d250c00d1..93a2a0932b5 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -1422,7 +1423,8 @@ void native_play_dead(void) tboot_shutdown(TB_SHUTDOWN_WFS); mwait_play_dead(); /* Only returns on failure */ - hlt_play_dead(); + if (cpuidle_play_dead()) + hlt_play_dead(); } #else /* ... !CONFIG_HOTPLUG_CPU */ diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0e8e2de2ed3..6b1d32a161a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -770,6 +770,35 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, return index; } + +/** + * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining) + * @dev: the target CPU + * @index: the index of suggested state + */ +static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) +{ + struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); + + ACPI_FLUSH_CPU_CACHE(); + + while (1) { + + if (cx->entry_method == ACPI_CSTATE_HALT) + halt(); + else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { + inb(cx->address); + /* See comment in acpi_idle_do_entry() */ + inl(acpi_gbl_FADT.xpm_timer_block.address); + } else + return -ENODEV; + } + + /* Never reached */ + return 0; +} + /** * acpi_idle_enter_simple - enters an ACPI state without BM handling * @dev: the target CPU @@ -1077,12 +1106,14 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_c1; + state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; break; case ACPI_STATE_C2: state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_simple; + state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; break; diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f7cab5e9c4d..3e146b2ada4 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -71,6 +71,34 @@ typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev, static cpuidle_enter_t cpuidle_enter_ops; +/** + * cpuidle_play_dead - cpu off-lining + * + * Only returns in case of an error + */ +int cpuidle_play_dead(void) +{ + struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); + struct cpuidle_driver *drv = cpuidle_get_driver(); + int i, dead_state = -1; + int power_usage = -1; + + /* Find lowest-power state that supports long-term idle */ + for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { + struct cpuidle_state *s = &drv->states[i]; + + if (s->power_usage < power_usage && s->enter_dead) { + power_usage = s->power_usage; + dead_state = i; + } + } + + if (dead_state != -1) + return drv->states[dead_state].enter_dead(dev, dead_state); + + return -ENODEV; +} + /** * cpuidle_idle_call - the main idle loop * diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index f3ebbba368b..d557bcd0ada 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -51,6 +51,8 @@ struct cpuidle_state { int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); + + int (*enter_dead) (struct cpuidle_device *dev, int index); }; /* Idle State Flags */ @@ -147,6 +149,8 @@ extern int cpuidle_wrap_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index, int (*enter)(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)); +extern int cpuidle_play_dead(void); + #else static inline void disable_cpuidle(void) { } static inline int cpuidle_idle_call(void) { return -ENODEV; } @@ -168,6 +172,7 @@ static inline int cpuidle_wrap_enter(struct cpuidle_device *dev, int (*enter)(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)) { return -ENODEV; } +static inline int cpuidle_play_dead(void) {return -ENODEV; } #endif -- cgit v1.2.3-70-g09d2 From c80f5b31f3c55a197f5323b93d1e3553429a427e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 15 Mar 2012 09:32:05 +0100 Subject: ACPI: processor_driver: add missing kfree The function acpi_processor_add is stored in the ops.add field of a acpi_driver structure. This function is then called in acpi_bus_driver_init. On failure, this function clears the field device->driver_data, but does not free its contents. Thus the free has to be done by the add function. In acpi_processor_add, the corresponding value is pr. This value is currently freed on failure before storing it in device->driver_data, but not after. This free is added in the error handling code at the end of the function. The per_cpu variable processors is also cleared so that it does not refer to a dangling pointer. Signed-off-by: Julia Lawall Reviewed-by: Srivatsa S. Bhat Acked-by: Deepthi Dharwar Signed-off-by: Len Brown --- drivers/acpi/processor_driver.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 8ae05ce1850..fce0066aa4a 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -535,8 +535,8 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) return -ENOMEM; if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) { - kfree(pr); - return -ENOMEM; + result = -ENOMEM; + goto err_free_pr; } pr->handle = device->handle; @@ -576,7 +576,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) dev = get_cpu_device(pr->id); if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) { result = -EFAULT; - goto err_free_cpumask; + goto err_clear_processor; } /* @@ -594,9 +594,15 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) err_remove_sysfs: sysfs_remove_link(&device->dev.kobj, "sysdev"); +err_clear_processor: + /* + * processor_device_array is not cleared to allow checks for buggy BIOS + */ + per_cpu(processors, pr->id) = NULL; err_free_cpumask: free_cpumask_var(pr->throttling.shared_cpu_map); - +err_free_pr: + kfree(pr); return result; } -- cgit v1.2.3-70-g09d2 From e252675fb722d4a307cc380a06a905f03cf9951c Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 24 Feb 2012 11:41:53 +0000 Subject: ACPI: consistently use should_use_kmap() ... so that acpi_unmap()'s behavior gets in sync with acpi_map()'s. Signed-off-by: Jan Beulich Signed-off-by: Len Brown --- drivers/acpi/osl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 412a1e04a92..5aef087d42d 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -347,7 +347,7 @@ static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr) unsigned long pfn; pfn = pg_off >> PAGE_SHIFT; - if (page_is_ram(pfn)) + if (should_use_kmap(pfn)) kunmap(pfn_to_page(pfn)); else iounmap(vaddr); -- cgit v1.2.3-70-g09d2 From 9505626d7bfeb5bd4b85acb483831ac640b2a5e8 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 28 Feb 2012 13:27:44 -0800 Subject: ACPI: Fix unprotected smp_processor_id() in acpi_processor_cst_has_changed() The acpi_processor_cst_has_changed() function is invoked from a CPU_ONLINE or CPU_DEAD function, which might well execute on CPU 0 even though the CPU being hotplugged is some other CPU. In addition, acpi_processor_cst_has_changed() invokes smp_processor_id() without protection, resulting in splats when onlining CPUs. This commit therefore changes the smp_processor_id() to pr->id, as is used elsewhere in the code, for example, in acpi_processor_add(). Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney Reviewed-by: Srivatsa S. Bhat Tested-by: Yong Zhang Acked-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0e8e2de2ed3..9e57b06d1f2 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1159,8 +1159,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) * to make the code that updates C-States be called once. */ - if (smp_processor_id() == 0 && - cpuidle_get_driver() == &acpi_idle_driver) { + if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) { cpuidle_pause_and_lock(); /* Protect against cpu-hotplug */ -- cgit v1.2.3-70-g09d2 From 37239978778806ecba54da60676abb46870acebb Mon Sep 17 00:00:00 2001 From: Alex He Date: Tue, 21 Feb 2012 16:58:10 +0800 Subject: ACPI: Clean redundant codes in scan.c Clean the redundant codes of apci_bus_get_power_flags(). Signed-off-by: Alex He Signed-off-by: Len Brown --- drivers/acpi/scan.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8ab80bafe3f..5d24a17aa85 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -880,7 +880,6 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) int j; device->power.flags.power_resources = 1; - ps->flags.valid = 1; for (j = 0; j < ps->resources.count; j++) acpi_bus_add_power_resource(ps->resources.handles[j]); } @@ -888,10 +887,8 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) /* Evaluate "_PSx" to see if we can do explicit sets */ object_name[2] = 'S'; status = acpi_get_handle(device->handle, object_name, &handle); - if (ACPI_SUCCESS(status)) { + if (ACPI_SUCCESS(status)) ps->flags.explicit_set = 1; - ps->flags.valid = 1; - } /* State is valid if we have some power control */ if (ps->resources.count || ps->flags.explicit_set) -- cgit v1.2.3-70-g09d2 From f2d4753fbd4d15c65d6ba48167aa83916ddbe518 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 31 Jan 2012 13:19:19 -0500 Subject: ACPI: export acpi_kobj Drivers may wish to add entries to /sys/firmware/acpi, so export acpi_kobj in order to let them do that. Signed-off-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/acpi/bus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9ecec98bc76..3263b68cdfa 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1010,6 +1010,7 @@ static int __init acpi_bus_init(void) } struct kobject *acpi_kobj; +EXPORT_SYMBOL_GPL(acpi_kobj); static int __init acpi_init(void) { -- cgit v1.2.3-70-g09d2 From d1ff4b1cdbabb9ab9813f3d6e1cbec42cc5d6ed8 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 31 Jan 2012 13:19:20 -0500 Subject: ACPI: Add support for exposing BGRT data ACPI 5.0 adds the BGRT, a table that contains a pointer to the firmware boot splash and associated metadata. This simple driver exposes it via /sys/firmware/acpi in order to allow bootsplash applications to draw their splash around the firmware image and reduce the number of jarring graphical transitions during boot. Signed-off-by: Matthew Garrett Signed-off-by: Len Brown --- Documentation/ABI/testing/sysfs-firmware-acpi | 20 +++ drivers/acpi/Kconfig | 9 ++ drivers/acpi/Makefile | 1 + drivers/acpi/bgrt.c | 175 ++++++++++++++++++++++++++ 4 files changed, 205 insertions(+) create mode 100644 drivers/acpi/bgrt.c (limited to 'drivers/acpi') diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index 4f9ba3c2fca..dd930c8db41 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -1,3 +1,23 @@ +What: /sys/firmware/acpi/bgrt/ +Date: January 2012 +Contact: Matthew Garrett +Description: + The BGRT is an ACPI 5.0 feature that allows the OS + to obtain a copy of the firmware boot splash and + some associated metadata. This is intended to be used + by boot splash applications in order to interact with + the firmware boot splash in order to avoid jarring + transitions. + + image: The image bitmap. Currently a 32-bit BMP. + status: 1 if the image is valid, 0 if firmware invalidated it. + type: 0 indicates image is in BMP format. + version: The version of the BGRT. Currently 1. + xoffset: The number of pixels between the left of the screen + and the left edge of the image. + yoffset: The number of pixels between the top of the screen + and the top edge of the image. + What: /sys/firmware/acpi/interrupts/ Date: February 2008 Contact: Len Brown diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 7556913aba4..47768ff8734 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -384,6 +384,15 @@ config ACPI_CUSTOM_METHOD load additional kernel modules after boot, this feature may be used to override that restriction). +config ACPI_BGRT + tristate "Boottime Graphics Resource Table support" + default n + help + This driver adds support for exposing the ACPI Boottime Graphics + Resource Table, which allows the operating system to obtain + data from the firmware boot splash. It will appear under + /sys/firmware/acpi/bgrt/ . + source "drivers/acpi/apei/Kconfig" endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 1567028d203..47199e2a913 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_ACPI_SBS) += sbs.o obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o +obj-$(CONFIG_ACPI_BGRT) += bgrt.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c new file mode 100644 index 00000000000..8cf6c46e99f --- /dev/null +++ b/drivers/acpi/bgrt.c @@ -0,0 +1,175 @@ +/* + * Copyright 2012 Red Hat, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct acpi_table_bgrt *bgrt_tab; +static struct kobject *bgrt_kobj; + +struct bmp_header { + u16 id; + u32 size; +} __attribute ((packed)); + +static struct bmp_header bmp_header; + +static ssize_t show_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->version); +} +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->status); +} +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +static ssize_t show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_type); +} +static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); + +static ssize_t show_xoffset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_x); +} +static DEVICE_ATTR(xoffset, S_IRUGO, show_xoffset, NULL); + +static ssize_t show_yoffset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_y); +} +static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL); + +static ssize_t show_image(struct file *file, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + int size = attr->size; + void __iomem *image = attr->private; + + if (off >= size) { + count = 0; + } else { + if (off + count > size) + count = size - off; + + memcpy_fromio(buf, image+off, count); + } + + return count; +} + +static struct bin_attribute image_attr = { + .attr = { + .name = "image", + .mode = S_IRUGO, + }, + .read = show_image, +}; + +static struct attribute *bgrt_attributes[] = { + &dev_attr_version.attr, + &dev_attr_status.attr, + &dev_attr_type.attr, + &dev_attr_xoffset.attr, + &dev_attr_yoffset.attr, + NULL, +}; + +static struct attribute_group bgrt_attribute_group = { + .attrs = bgrt_attributes, +}; + +static int __init bgrt_init(void) +{ + acpi_status status; + int ret; + void __iomem *bgrt; + + if (acpi_disabled) + return -ENODEV; + + status = acpi_get_table("BGRT", 0, + (struct acpi_table_header **)&bgrt_tab); + + if (ACPI_FAILURE(status)) + return -ENODEV; + + sysfs_bin_attr_init(&image_attr); + + bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header)); + + if (!bgrt) { + ret = -EINVAL; + goto out_err; + } + + memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header)); + image_attr.size = bmp_header.size; + iounmap(bgrt); + + image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size); + + if (!image_attr.private) { + ret = -EINVAL; + goto out_err; + } + + + bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj); + if (!bgrt_kobj) { + ret = -EINVAL; + goto out_iounmap; + } + + ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group); + if (ret) + goto out_kobject; + + ret = sysfs_create_bin_file(bgrt_kobj, &image_attr); + if (ret) + goto out_group; + + return 0; + +out_group: + sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group); +out_kobject: + kobject_put(bgrt_kobj); +out_iounmap: + iounmap(image_attr.private); +out_err: + return ret; +} + +static void __exit bgrt_exit(void) +{ + iounmap(image_attr.private); + sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group); + sysfs_remove_bin_file(bgrt_kobj, &image_attr); +} + +module_init(bgrt_init); +module_exit(bgrt_exit); + +MODULE_AUTHOR("Matthew Garrett"); +MODULE_DESCRIPTION("BGRT boot graphic support"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From ea9f8856bd6d4ed45885b06a338f7362cd6c60e5 Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Fri, 30 Mar 2012 21:32:08 +0400 Subject: ACPI video: Harden video bus adding. It is always better to check return values, so add some new checks and correct existing ones. v2: Be consistent and don't mix errors from -E* and AE_* namespaces. Signed-off-by: Igor Murzov Signed-off-by: Len Brown --- drivers/acpi/video.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index eaef02afc7c..462486b9f9b 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -548,27 +548,27 @@ acpi_video_device_EDID(struct acpi_video_device *device, * 1. The system BIOS should NOT automatically control the brightness * level of the LCD when the power changes from AC to DC. * Return Value: - * -1 wrong arg. + * -EINVAL wrong arg. */ static int acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) { - u64 status = 0; + acpi_status status; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; - if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) { - status = -1; - goto Failed; - } + if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) + return -EINVAL; arg0.integer.value = (lcd_flag << 2) | bios_flag; video->dos_setting = arg0.integer.value; - acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL); + status = acpi_evaluate_object(video->device->handle, "_DOS", + &args, NULL); + if (ACPI_FAILURE(status)) + return -EIO; - Failed: - return status; + return 0; } /* @@ -1343,15 +1343,17 @@ static int acpi_video_bus_get_devices(struct acpi_video_bus *video, struct acpi_device *device) { - int status = 0; + int status; struct acpi_device *dev; - acpi_video_device_enumerate(video); + status = acpi_video_device_enumerate(video); + if (status) + return status; list_for_each_entry(dev, &device->children, node) { status = acpi_video_bus_get_one_device(dev, video); - if (ACPI_FAILURE(status)) { + if (status) { printk(KERN_WARNING PREFIX "Can't attach device\n"); continue; @@ -1653,8 +1655,12 @@ static int acpi_video_bus_add(struct acpi_device *device) mutex_init(&video->device_list_lock); INIT_LIST_HEAD(&video->video_device_list); - acpi_video_bus_get_devices(video, device); - acpi_video_bus_start_devices(video); + error = acpi_video_bus_get_devices(video, device); + if (error) + goto err_free_video; + error = acpi_video_bus_start_devices(video); + if (error) + goto err_put_video; video->input = input = input_allocate_device(); if (!input) { @@ -1692,14 +1698,19 @@ static int acpi_video_bus_add(struct acpi_device *device) video->pm_nb.notifier_call = acpi_video_resume; video->pm_nb.priority = 0; - register_pm_notifier(&video->pm_nb); + error = register_pm_notifier(&video->pm_nb); + if (error) + goto err_unregister_input_dev; return 0; + err_unregister_input_dev: + input_unregister_device(input); err_free_input_dev: input_free_device(input); err_stop_video: acpi_video_bus_stop_devices(video); + err_put_video: acpi_video_bus_put_devices(video); kfree(video->attached_array); err_free_video: -- cgit v1.2.3-70-g09d2 From b60e7f6166857c76871977794fa266b02da1f394 Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Fri, 30 Mar 2012 21:32:09 +0400 Subject: ACPI video: Don't start video device until its associated input device has been allocated Quoth Dmitry Torokhov: In addition to bus notifier we do install device notifier explicitly so it might fire up early. The easiest fox would be to move acpi_video_bus_start_devices() after input_allocate_device() but before input_register_device() - unregistered input devices can handle input_event() calls just fine. May fix crashes reported in: https://bugzilla.kernel.org/show_bug.cgi?id=40672 Signed-off-by: Igor Murzov Signed-off-by: Len Brown --- drivers/acpi/video.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 462486b9f9b..9577b6fa265 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1658,16 +1658,17 @@ static int acpi_video_bus_add(struct acpi_device *device) error = acpi_video_bus_get_devices(video, device); if (error) goto err_free_video; - error = acpi_video_bus_start_devices(video); - if (error) - goto err_put_video; video->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; - goto err_stop_video; + goto err_put_video; } + error = acpi_video_bus_start_devices(video); + if (error) + goto err_free_input_dev; + snprintf(video->phys, sizeof(video->phys), "%s/video/input0", acpi_device_hid(video->device)); @@ -1688,7 +1689,7 @@ static int acpi_video_bus_add(struct acpi_device *device) error = input_register_device(input); if (error) - goto err_free_input_dev; + goto err_stop_video; printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), @@ -1706,10 +1707,10 @@ static int acpi_video_bus_add(struct acpi_device *device) err_unregister_input_dev: input_unregister_device(input); - err_free_input_dev: - input_free_device(input); err_stop_video: acpi_video_bus_stop_devices(video); + err_free_input_dev: + input_free_device(input); err_put_video: acpi_video_bus_put_devices(video); kfree(video->attached_array); -- cgit v1.2.3-70-g09d2 From 344e222edf486bf42da1ced137e36df7a345b0ad Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Mar 2012 14:57:36 +0300 Subject: ACPI throttling: fix endian bug in acpi_read_throttling_status() Using a u64 here creates an endian bug. We store a u32 number in the top byte which is a larger number than intended on big endian systems. There is no reason to use a 64 bit data type here, I guess it was just an oversight. I removed the initialization to zero as well. It's needed with a u64 but with a u32, the variable gets initialized properly inside the call to acpi_os_read_port(). Signed-off-by: Dan Carpenter Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 605a2954ef1..1d02b7b5ade 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -769,7 +769,7 @@ static int acpi_read_throttling_status(struct acpi_processor *pr, u64 *value) { u32 bit_width, bit_offset; - u64 ptc_value; + u32 ptc_value; u64 ptc_mask; struct acpi_processor_throttling *throttling; int ret = -1; @@ -777,12 +777,11 @@ static int acpi_read_throttling_status(struct acpi_processor *pr, throttling = &pr->throttling; switch (throttling->status_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: - ptc_value = 0; bit_width = throttling->status_register.bit_width; bit_offset = throttling->status_register.bit_offset; acpi_os_read_port((acpi_io_address) throttling->status_register. - address, (u32 *) &ptc_value, + address, &ptc_value, (u32) (bit_width + bit_offset)); ptc_mask = (1 << bit_width) - 1; *value = (u64) ((ptc_value >> bit_offset) & ptc_mask); -- cgit v1.2.3-70-g09d2