Merge branches 'acpi-ec', 'acpi-cppc', 'acpi-fan' and 'acpi-battery'

Merge ACPI EC driver changes, CPPC-related changes, ACPI fan driver
changes and ACPI battery driver changes for 5.18-rc1:

 - Make wakeup events checks in the ACPI EC driver more
   straightforward and clean up acpi_ec_submit_event() (Rafael
   Wysocki).

 - Make it possible to obtain the CPU capacity with the help of CPPC
   information (Ionela Voinescu).

 - Improve fine grained fan control in the ACPI fan driver and
   document it (Srinivas Pandruvada).

 - Add device HID and quirk for Microsoft Surface Go 3 to the ACPI
   battery driver (Maximilian Luz).

* acpi-ec:
  ACPI: EC: Rearrange code in acpi_ec_submit_event()
  ACPI: EC: Reduce indentation level in acpi_ec_submit_event()
  ACPI: EC: Do not return result from advance_transaction()

* acpi-cppc:
  arm64, topology: enable use of init_cpu_capacity_cppc()
  arch_topology: obtain cpu capacity using information from CPPC
  x86, ACPI: rename init_freq_invariance_cppc() to arch_init_invariance_cppc()

* acpi-fan:
  Documentation/admin-guide/acpi: Add documentation for fine grain control
  ACPI: fan: Add additional attributes for fine grain control
  ACPI: fan: Properly handle fine grain control
  ACPI: fan: Optimize struct acpi_fan_fif
  ACPI: fan: Separate file for attributes creation
  ACPI: fan: Fix error reporting to user space

* acpi-battery:
  ACPI: battery: Add device HID and quirk for Microsoft Surface Go 3
This commit is contained in:
Rafael J. Wysocki 2022-03-18 17:36:54 +01:00
12 changed files with 410 additions and 166 deletions

View File

@ -60,3 +60,31 @@ For example::
When a given field is not populated or its value provided by the platform
firmware is invalid, the "not-defined" string is shown instead of the value.
ACPI Fan Fine Grain Control
=============================
When _FIF object specifies support for fine grain control, then fan speed
can be set from 0 to 100% with the recommended minimum "step size" via
_FSL object. User can adjust fan speed using thermal sysfs cooling device.
Here use can look at fan performance states for a reference speed (speed_rpm)
and set it by changing cooling device cur_state. If the fine grain control
is supported then user can also adjust to some other speeds which are
not defined in the performance states.
The support of fine grain control is presented via sysfs attribute
"fine_grain_control". If fine grain control is present, this attribute
will show "1" otherwise "0".
This sysfs attribute is presented in the same directory as performance states.
ACPI Fan Performance Feedback
=============================
The optional _FST object provides status information for the fan device.
This includes field to provide current fan speed in revolutions per minute
at which the fan is rotating.
This speed is presented in the sysfs using the attribute "fan_speed_rpm",
in the same directory as performance states.

View File

@ -24,6 +24,10 @@ void update_freq_counters_refs(void);
#define arch_scale_freq_capacity topology_get_freq_scale
#define arch_scale_freq_invariant topology_scale_freq_invariant
#ifdef CONFIG_ACPI_CPPC_LIB
#define arch_init_invariance_cppc topology_init_cpu_capacity_cppc
#endif
/* Replace task scheduler's default cpu-invariant accounting */
#define arch_scale_cpu_capacity topology_get_cpu_scale

View File

@ -227,7 +227,7 @@ static inline void init_freq_invariance(bool secondary, bool cppc_ready)
#ifdef CONFIG_ACPI_CPPC_LIB
void init_freq_invariance_cppc(void);
#define init_freq_invariance_cppc init_freq_invariance_cppc
#define arch_init_invariance_cppc init_freq_invariance_cppc
bool amd_set_max_freq_ratio(u64 *ratio);
#else

View File

@ -81,6 +81,9 @@ obj-$(CONFIG_ACPI_AC) += ac.o
obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_TINY_POWER_BUTTON) += tiny-power-button.o
obj-$(CONFIG_ACPI_FAN) += fan.o
fan-objs := fan_core.o
fan-objs += fan_attr.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-$(CONFIG_ACPI_TAD) += acpi_tad.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o

View File

@ -59,6 +59,10 @@ MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
static const struct acpi_device_id battery_device_ids[] = {
{"PNP0C0A", 0},
/* Microsoft Surface Go 3 */
{"MSHW0146", 0},
{"", 0},
};
@ -1148,6 +1152,14 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad"),
},
},
{
/* Microsoft Surface Go 3 */
.callback = battery_notification_delay_quirk,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"),
},
},
{},
};

View File

@ -633,8 +633,8 @@ static bool is_cppc_supported(int revision, int num_ent)
* )
*/
#ifndef init_freq_invariance_cppc
static inline void init_freq_invariance_cppc(void) { }
#ifndef arch_init_invariance_cppc
static inline void arch_init_invariance_cppc(void) { }
#endif
/**
@ -819,7 +819,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
}
init_freq_invariance_cppc();
arch_init_invariance_cppc();
kfree(output.pointer);
return 0;

View File

@ -168,7 +168,7 @@ struct acpi_ec_query {
};
static int acpi_ec_submit_query(struct acpi_ec *ec);
static bool advance_transaction(struct acpi_ec *ec, bool interrupt);
static void advance_transaction(struct acpi_ec *ec, bool interrupt);
static void acpi_ec_event_handler(struct work_struct *work);
struct acpi_ec *first_ec;
@ -441,36 +441,35 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
return true;
}
static bool acpi_ec_submit_event(struct acpi_ec *ec)
static void acpi_ec_submit_event(struct acpi_ec *ec)
{
/*
* It is safe to mask the events here, because acpi_ec_close_event()
* will run at least once after this.
*/
acpi_ec_mask_events(ec);
if (!acpi_ec_event_enabled(ec))
return false;
return;
if (ec->event_state == EC_EVENT_READY) {
ec_dbg_evt("Command(%s) submitted/blocked",
acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
if (ec->event_state != EC_EVENT_READY)
return;
ec->event_state = EC_EVENT_IN_PROGRESS;
/*
* If events_to_process is greqter than 0 at this point, the
* while () loop in acpi_ec_event_handler() is still running
* and incrementing events_to_process will cause it to invoke
* acpi_ec_submit_query() once more, so it is not necessary to
* queue up the event work to start the same loop again.
*/
if (ec->events_to_process++ > 0)
return true;
ec->events_in_progress++;
return queue_work(ec_wq, &ec->work);
}
ec_dbg_evt("Command(%s) submitted/blocked",
acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
ec->event_state = EC_EVENT_IN_PROGRESS;
/*
* The event handling work has not been completed yet, so it needs to be
* flushed.
* If events_to_process is greater than 0 at this point, the while ()
* loop in acpi_ec_event_handler() is still running and incrementing
* events_to_process will cause it to invoke acpi_ec_submit_query() once
* more, so it is not necessary to queue up the event work to start the
* same loop again.
*/
return true;
if (ec->events_to_process++ > 0)
return;
ec->events_in_progress++;
queue_work(ec_wq, &ec->work);
}
static void acpi_ec_complete_event(struct acpi_ec *ec)
@ -655,11 +654,10 @@ static void acpi_ec_spurious_interrupt(struct acpi_ec *ec, struct transaction *t
acpi_ec_mask_events(ec);
}
static bool advance_transaction(struct acpi_ec *ec, bool interrupt)
static void advance_transaction(struct acpi_ec *ec, bool interrupt)
{
struct transaction *t = ec->curr;
bool wakeup = false;
bool ret = false;
u8 status;
ec_dbg_stm("%s (%d)", interrupt ? "IRQ" : "TASK", smp_processor_id());
@ -724,12 +722,10 @@ static bool advance_transaction(struct acpi_ec *ec, bool interrupt)
out:
if (status & ACPI_EC_FLAG_SCI)
ret = acpi_ec_submit_event(ec);
acpi_ec_submit_event(ec);
if (wakeup && interrupt)
wake_up(&ec->wait);
return ret;
}
static void start_transaction(struct acpi_ec *ec)
@ -1242,6 +1238,7 @@ static void acpi_ec_event_handler(struct work_struct *work)
acpi_ec_submit_query(ec);
spin_lock_irq(&ec->lock);
ec->events_to_process--;
}
@ -1250,27 +1247,30 @@ static void acpi_ec_event_handler(struct work_struct *work)
* event handling work again regardless of whether or not the query
* queued up above is processed successfully.
*/
if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT)
if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) {
bool guard_timeout;
acpi_ec_complete_event(ec);
else
acpi_ec_close_event(ec);
spin_unlock_irq(&ec->lock);
ec_dbg_evt("Event stopped");
ec_dbg_evt("Event stopped");
spin_unlock_irq(&ec->lock);
guard_timeout = !!ec_guard(ec);
if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && ec_guard(ec)) {
spin_lock_irq(&ec->lock);
/* Take care of SCI_EVT unless someone else is doing that. */
if (!ec->curr)
if (guard_timeout && !ec->curr)
advance_transaction(ec, false);
} else {
acpi_ec_close_event(ec);
spin_unlock_irq(&ec->lock);
ec_dbg_evt("Event stopped");
}
spin_lock_irq(&ec->lock);
ec->events_in_progress--;
spin_unlock_irq(&ec->lock);
}
@ -2051,6 +2051,11 @@ void acpi_ec_set_gpe_wake_mask(u8 action)
acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action);
}
static bool acpi_ec_work_in_progress(struct acpi_ec *ec)
{
return ec->events_in_progress + ec->queries_in_progress > 0;
}
bool acpi_ec_dispatch_gpe(void)
{
bool work_in_progress = false;
@ -2084,7 +2089,8 @@ bool acpi_ec_dispatch_gpe(void)
if (acpi_ec_gpe_status_set(first_ec)) {
pm_pr_dbg("ACPI EC GPE status set\n");
work_in_progress = advance_transaction(first_ec, false);
advance_transaction(first_ec, false);
work_in_progress = acpi_ec_work_in_progress(first_ec);
}
spin_unlock_irq(&first_ec->lock);
@ -2102,8 +2108,7 @@ bool acpi_ec_dispatch_gpe(void)
spin_lock_irq(&first_ec->lock);
work_in_progress = first_ec->events_in_progress +
first_ec->queries_in_progress > 0;
work_in_progress = acpi_ec_work_in_progress(first_ec);
spin_unlock_irq(&first_ec->lock);
} while (work_in_progress && !pm_wakeup_pending());

View File

@ -6,9 +6,53 @@
*
* Add new device IDs before the generic ACPI fan one.
*/
#ifndef _ACPI_FAN_H_
#define _ACPI_FAN_H_
#define ACPI_FAN_DEVICE_IDS \
{"INT3404", }, /* Fan */ \
{"INTC1044", }, /* Fan for Tiger Lake generation */ \
{"INTC1048", }, /* Fan for Alder Lake generation */ \
{"INTC10A2", }, /* Fan for Raptor Lake generation */ \
{"PNP0C0B", } /* Generic ACPI fan */
#define ACPI_FPS_NAME_LEN 20
struct acpi_fan_fps {
u64 control;
u64 trip_point;
u64 speed;
u64 noise_level;
u64 power;
char name[ACPI_FPS_NAME_LEN];
struct device_attribute dev_attr;
};
struct acpi_fan_fif {
u8 revision;
u8 fine_grain_ctrl;
u8 step_size;
u8 low_speed_notification;
};
struct acpi_fan_fst {
u64 revision;
u64 control;
u64 speed;
};
struct acpi_fan {
bool acpi4;
struct acpi_fan_fif fif;
struct acpi_fan_fps *fps;
int fps_count;
struct thermal_cooling_device *cdev;
struct device_attribute fst_speed;
struct device_attribute fine_grain_control;
};
int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst);
int acpi_fan_create_attributes(struct acpi_device *device);
void acpi_fan_delete_attributes(struct acpi_device *device);
#endif

137
drivers/acpi/fan_attr.c Normal file
View File

@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* fan_attr.c - Create extra attributes for ACPI Fan driver
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2022 Intel Corporation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include "fan.h"
MODULE_LICENSE("GPL");
static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf)
{
struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr);
int count;
if (fps->control == 0xFFFFFFFF || fps->control > 100)
count = scnprintf(buf, PAGE_SIZE, "not-defined:");
else
count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control);
if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point);
if (fps->speed == 0xFFFFFFFF)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed);
if (fps->noise_level == 0xFFFFFFFF)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100);
if (fps->power == 0xFFFFFFFF)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power);
return count;
}
static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf)
{
struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
struct acpi_fan_fst fst;
int status;
status = acpi_fan_get_fst(acpi_dev, &fst);
if (status)
return status;
return sprintf(buf, "%lld\n", fst.speed);
}
static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf)
{
struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
struct acpi_fan *fan = acpi_driver_data(acpi_dev);
return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl);
}
int acpi_fan_create_attributes(struct acpi_device *device)
{
struct acpi_fan *fan = acpi_driver_data(device);
int i, status;
sysfs_attr_init(&fan->fine_grain_control.attr);
fan->fine_grain_control.show = show_fine_grain_control;
fan->fine_grain_control.store = NULL;
fan->fine_grain_control.attr.name = "fine_grain_control";
fan->fine_grain_control.attr.mode = 0444;
status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr);
if (status)
return status;
/* _FST is present if we are here */
sysfs_attr_init(&fan->fst_speed.attr);
fan->fst_speed.show = show_fan_speed;
fan->fst_speed.store = NULL;
fan->fst_speed.attr.name = "fan_speed_rpm";
fan->fst_speed.attr.mode = 0444;
status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr);
if (status)
goto rem_fine_grain_attr;
for (i = 0; i < fan->fps_count; ++i) {
struct acpi_fan_fps *fps = &fan->fps[i];
snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i);
sysfs_attr_init(&fps->dev_attr.attr);
fps->dev_attr.show = show_state;
fps->dev_attr.store = NULL;
fps->dev_attr.attr.name = fps->name;
fps->dev_attr.attr.mode = 0444;
status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr);
if (status) {
int j;
for (j = 0; j < i; ++j)
sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
goto rem_fst_attr;
}
}
return 0;
rem_fst_attr:
sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
rem_fine_grain_attr:
sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
return status;
}
void acpi_fan_delete_attributes(struct acpi_device *device)
{
struct acpi_fan *fan = acpi_driver_data(device);
int i;
for (i = 0; i < fan->fps_count; ++i)
sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
}

View File

@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* acpi_fan.c - ACPI Fan Driver ($Revision: 29 $)
* fan_core.c - ACPI Fan core Driver
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2022 Intel Corporation. All rights reserved.
*/
#include <linux/kernel.h>
@ -45,33 +46,6 @@ static const struct dev_pm_ops acpi_fan_pm = {
#define FAN_PM_OPS_PTR NULL
#endif
#define ACPI_FPS_NAME_LEN 20
struct acpi_fan_fps {
u64 control;
u64 trip_point;
u64 speed;
u64 noise_level;
u64 power;
char name[ACPI_FPS_NAME_LEN];
struct device_attribute dev_attr;
};
struct acpi_fan_fif {
u64 revision;
u64 fine_grain_ctrl;
u64 step_size;
u64 low_speed_notification;
};
struct acpi_fan {
bool acpi4;
struct acpi_fan_fif fif;
struct acpi_fan_fps *fps;
int fps_count;
struct thermal_cooling_device *cdev;
};
static struct platform_driver acpi_fan_driver = {
.probe = acpi_fan_probe,
.remove = acpi_fan_remove,
@ -89,25 +63,29 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
struct acpi_device *device = cdev->devdata;
struct acpi_fan *fan = acpi_driver_data(device);
if (fan->acpi4)
*state = fan->fps_count - 1;
else
if (fan->acpi4) {
if (fan->fif.fine_grain_ctrl)
*state = 100 / fan->fif.step_size;
else
*state = fan->fps_count - 1;
} else {
*state = 1;
}
return 0;
}
static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_fan *fan = acpi_driver_data(device);
union acpi_object *obj;
acpi_status status;
int control, i;
int ret = 0;
status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
if (ACPI_FAILURE(status)) {
dev_err(&device->dev, "Get fan state failed\n");
return status;
return -ENODEV;
}
obj = buffer.pointer;
@ -115,35 +93,52 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
obj->package.count != 3 ||
obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
dev_err(&device->dev, "Invalid _FST data\n");
status = -EINVAL;
ret = -EINVAL;
goto err;
}
control = obj->package.elements[1].integer.value;
for (i = 0; i < fan->fps_count; i++) {
/*
* When Fine Grain Control is set, return the state
* corresponding to maximum fan->fps[i].control
* value compared to the current speed. Here the
* fan->fps[] is sorted array with increasing speed.
*/
if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) {
i = (i > 0) ? i - 1 : 0;
break;
} else if (control == fan->fps[i].control) {
break;
fst->revision = obj->package.elements[0].integer.value;
fst->control = obj->package.elements[1].integer.value;
fst->speed = obj->package.elements[2].integer.value;
err:
kfree(obj);
return ret;
}
static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
{
struct acpi_fan *fan = acpi_driver_data(device);
struct acpi_fan_fst fst;
int status, i;
status = acpi_fan_get_fst(device, &fst);
if (status)
return status;
if (fan->fif.fine_grain_ctrl) {
/* This control should be same what we set using _FSL by spec */
if (fst.control > 100) {
dev_dbg(&device->dev, "Invalid control value returned\n");
goto match_fps;
}
*state = (int) fst.control / fan->fif.step_size;
return 0;
}
match_fps:
for (i = 0; i < fan->fps_count; i++) {
if (fst.control == fan->fps[i].control)
break;
}
if (i == fan->fps_count) {
dev_dbg(&device->dev, "Invalid control value returned\n");
status = -EINVAL;
goto err;
return -EINVAL;
}
*state = i;
err:
kfree(obj);
return status;
}
@ -187,15 +182,30 @@ static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
{
struct acpi_fan *fan = acpi_driver_data(device);
acpi_status status;
u64 value = state;
int max_state;
if (state >= fan->fps_count)
if (fan->fif.fine_grain_ctrl)
max_state = 100 / fan->fif.step_size;
else
max_state = fan->fps_count - 1;
if (state > max_state)
return -EINVAL;
status = acpi_execute_simple_method(device->handle, "_FSL",
fan->fps[state].control);
if (fan->fif.fine_grain_ctrl) {
value *= fan->fif.step_size;
/* Spec allows compensate the last step only */
if (value + fan->fif.step_size > 100)
value = 100;
} else {
value = fan->fps[state].control;
}
status = acpi_execute_simple_method(device->handle, "_FSL", value);
if (ACPI_FAILURE(status)) {
dev_dbg(&device->dev, "Failed to set state by _FSL\n");
return status;
return -ENODEV;
}
return 0;
@ -237,7 +247,8 @@ static int acpi_fan_get_fif(struct acpi_device *device)
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_fan *fan = acpi_driver_data(device);
struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif };
u64 fields[4];
struct acpi_buffer fif = { sizeof(fields), fields };
union acpi_object *obj;
acpi_status status;
@ -258,6 +269,17 @@ static int acpi_fan_get_fif(struct acpi_device *device)
status = -EINVAL;
}
fan->fif.revision = fields[0];
fan->fif.fine_grain_ctrl = fields[1];
fan->fif.step_size = fields[2];
fan->fif.low_speed_notification = fields[3];
/* If there is a bug in step size and set as 0, change to 1 */
if (!fan->fif.step_size)
fan->fif.step_size = 1;
/* If step size > 9, change to 9 (by spec valid values 1-9) */
else if (fan->fif.step_size > 9)
fan->fif.step_size = 9;
err:
kfree(obj);
return status;
@ -270,39 +292,6 @@ static int acpi_fan_speed_cmp(const void *a, const void *b)
return fps1->speed - fps2->speed;
}
static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf)
{
struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr);
int count;
if (fps->control == 0xFFFFFFFF || fps->control > 100)
count = scnprintf(buf, PAGE_SIZE, "not-defined:");
else
count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control);
if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point);
if (fps->speed == 0xFFFFFFFF)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed);
if (fps->noise_level == 0xFFFFFFFF)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100);
if (fps->power == 0xFFFFFFFF)
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n");
else
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power);
return count;
}
static int acpi_fan_get_fps(struct acpi_device *device)
{
struct acpi_fan *fan = acpi_driver_data(device);
@ -347,25 +336,6 @@ static int acpi_fan_get_fps(struct acpi_device *device)
sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
acpi_fan_speed_cmp, NULL);
for (i = 0; i < fan->fps_count; ++i) {
struct acpi_fan_fps *fps = &fan->fps[i];
snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i);
sysfs_attr_init(&fps->dev_attr.attr);
fps->dev_attr.show = show_state;
fps->dev_attr.store = NULL;
fps->dev_attr.attr.name = fps->name;
fps->dev_attr.attr.mode = 0444;
status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr);
if (status) {
int j;
for (j = 0; j < i; ++j)
sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
break;
}
}
err:
kfree(obj);
return status;
@ -396,6 +366,10 @@ static int acpi_fan_probe(struct platform_device *pdev)
if (result)
return result;
result = acpi_fan_create_attributes(device);
if (result)
return result;
fan->acpi4 = true;
} else {
result = acpi_device_update_power(device, NULL);
@ -437,12 +411,8 @@ static int acpi_fan_probe(struct platform_device *pdev)
return 0;
err_end:
if (fan->acpi4) {
int i;
for (i = 0; i < fan->fps_count; ++i)
sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
}
if (fan->acpi4)
acpi_fan_delete_attributes(device);
return result;
}
@ -453,10 +423,8 @@ static int acpi_fan_remove(struct platform_device *pdev)
if (fan->acpi4) {
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
int i;
for (i = 0; i < fan->fps_count; ++i)
sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
acpi_fan_delete_attributes(device);
}
sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
sysfs_remove_link(&fan->cdev->device.kobj, "device");

View File

@ -339,6 +339,46 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
return !ret;
}
#ifdef CONFIG_ACPI_CPPC_LIB
#include <acpi/cppc_acpi.h>
void topology_init_cpu_capacity_cppc(void)
{
struct cppc_perf_caps perf_caps;
int cpu;
if (likely(acpi_disabled || !acpi_cpc_valid()))
return;
raw_capacity = kcalloc(num_possible_cpus(), sizeof(*raw_capacity),
GFP_KERNEL);
if (!raw_capacity)
return;
for_each_possible_cpu(cpu) {
if (!cppc_get_perf_caps(cpu, &perf_caps) &&
(perf_caps.highest_perf >= perf_caps.nominal_perf) &&
(perf_caps.highest_perf >= perf_caps.lowest_perf)) {
raw_capacity[cpu] = perf_caps.highest_perf;
pr_debug("cpu_capacity: CPU%d cpu_capacity=%u (raw).\n",
cpu, raw_capacity[cpu]);
continue;
}
pr_err("cpu_capacity: CPU%d missing/invalid highest performance.\n", cpu);
pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
goto exit;
}
topology_normalize_cpu_scale();
schedule_work(&update_topology_flags_work);
pr_debug("cpu_capacity: cpu_capacity initialization done\n");
exit:
free_raw_capacity();
}
#endif
#ifdef CONFIG_CPU_FREQ
static cpumask_var_t cpus_to_visit;
static void parsing_done_workfn(struct work_struct *work);
@ -387,9 +427,8 @@ static int __init register_cpufreq_notifier(void)
int ret;
/*
* on ACPI-based systems we need to use the default cpu capacity
* until we have the necessary code to parse the cpu capacity, so
* skip registering cpufreq notifier.
* On ACPI-based systems skip registering cpufreq notifier as cpufreq
* information is not needed for cpu capacity initialization.
*/
if (!acpi_disabled || !raw_capacity)
return -EINVAL;

View File

@ -11,6 +11,10 @@
void topology_normalize_cpu_scale(void);
int topology_update_cpu_topology(void);
#ifdef CONFIG_ACPI_CPPC_LIB
void topology_init_cpu_capacity_cppc(void);
#endif
struct device_node;
bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu);