summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/battery.c147
-rw-r--r--drivers/acpi/fan.h27
-rw-r--r--drivers/acpi/scan.c4
-rw-r--r--drivers/acpi/utils.c17
-rw-r--r--drivers/acpi/video_detect.c7
-rw-r--r--drivers/i2c/i2c-core-acpi.c15
-rw-r--r--drivers/pci/vgaarb.c4
-rw-r--r--drivers/platform/x86/lenovo/thinkpad_acpi.c2
-rw-r--r--include/linux/acpi.h1
9 files changed, 162 insertions, 62 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index f5e0eb299610..0084f308b790 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -10,10 +10,12 @@
#define pr_fmt(fmt) "ACPI: battery: " fmt
+#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
+#include <linux/kfifo.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -21,6 +23,7 @@
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/types.h>
+#include <linux/workqueue.h>
#include <linux/unaligned.h>
@@ -43,6 +46,9 @@
#define MAX_STRING_LENGTH 64
+#define MAX_QUEUED_EVENTS 16
+#define NOTIF_MERGING_MS 10
+
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
MODULE_DESCRIPTION("ACPI Battery Driver");
@@ -95,6 +101,8 @@ struct acpi_battery {
struct power_supply_desc bat_desc;
struct acpi_device *device;
struct device *phys_dev;
+ struct kfifo acpi_notif_fifo;
+ struct delayed_work acpi_notif_dwork;
struct notifier_block pm_nb;
struct list_head list;
unsigned long update_time;
@@ -150,27 +158,28 @@ static int acpi_battery_technology(struct acpi_battery *battery)
static int acpi_battery_get_state(struct acpi_battery *battery);
-static int acpi_battery_is_charged(struct acpi_battery *battery)
+static bool acpi_battery_is_full(struct acpi_battery *battery)
{
- /* charging, discharging, critical low or charge limited */
- if (battery->state != 0)
- return 0;
-
/* battery not reporting charge */
if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
battery->capacity_now == 0)
- return 0;
+ return false;
/* good batteries update full_charge as the batteries degrade */
if (battery->full_charge_capacity == battery->capacity_now)
- return 1;
+ return true;
/* fallback to using design values for broken batteries */
- if (battery->design_capacity <= battery->capacity_now)
- return 1;
+ return battery->design_capacity <= battery->capacity_now;
+}
- /* we don't do any sort of metric based on percentages */
- return 0;
+static int acpi_battery_is_charged(struct acpi_battery *battery)
+{
+ /* charging, discharging, critical low or charge limited */
+ if (battery->state != 0)
+ return 0;
+
+ return acpi_battery_is_full(battery);
}
static bool acpi_battery_is_degraded(struct acpi_battery *battery)
@@ -211,13 +220,14 @@ static int acpi_battery_get_property(struct power_supply *psy,
if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
val->intval = acpi_battery_handle_discharging(battery);
else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
- /* Validate the status by checking the current. */
- if (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
- battery->rate_now == 0) {
- /* On charge but no current (0W/0mA). */
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- } else {
+ /* Check the rate and capacity to validate the status. */
+ if (!acpi_battery_is_full(battery) ||
+ (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+ battery->rate_now > 0)) {
val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ /* Full and zero rate. */
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
else if (battery->state & ACPI_BATTERY_STATE_CHARGE_LIMITING)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -483,6 +493,15 @@ static int acpi_battery_get_status(struct acpi_battery *battery)
return 0;
}
+static void acpi_battery_clean_unprintable_chars(char *str, size_t length)
+{
+ for (unsigned int i = 0; i < length; i++) {
+ if (!isascii(str[i]) || !isprint(str[i])) {
+ str[i] = '\0';
+ break;
+ }
+ }
+}
static int extract_battery_info(const int use_bix,
struct acpi_battery *battery,
@@ -524,6 +543,10 @@ static int extract_battery_info(const int use_bix,
battery->capacity_now > battery->full_charge_capacity)
battery->capacity_now = battery->full_charge_capacity;
+ if (!result)
+ acpi_battery_clean_unprintable_chars(battery->model_number,
+ ARRAY_SIZE(battery->model_number));
+
return result;
}
@@ -668,9 +691,13 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
{
unsigned long x;
struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+ int err;
+
+ err = kstrtoul(buf, 10, &x);
+ if (err)
+ return err;
- if (sscanf(buf, "%lu\n", &x) == 1)
- battery->alarm = x/1000;
+ battery->alarm = x / 1000;
if (acpi_battery_present(battery))
acpi_battery_set_alarm(battery);
return count;
@@ -1059,14 +1086,24 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
}
/* Driver Interface */
-static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_battery_notification_worker(struct work_struct *work)
{
- struct acpi_battery *battery = data;
+ struct acpi_battery *battery = container_of(work, struct acpi_battery,
+ acpi_notif_dwork.work);
struct acpi_device *device = battery->device;
+ u32 events[MAX_QUEUED_EVENTS];
struct power_supply *old;
+ unsigned int count, i;
guard(mutex)(&battery->update_lock);
+ count = kfifo_out(&battery->acpi_notif_fifo, events, sizeof(events));
+ count /= sizeof(events[0]);
+ if (!count)
+ return;
+
+ pr_debug("merged %u battery notifications within %dms\n", count, NOTIF_MERGING_MS);
+
old = battery->bat;
/*
* On Acer Aspire V5-573G notifications are sometimes triggered too
@@ -1076,19 +1113,46 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
*/
if (battery_notification_delay_ms > 0)
msleep(battery_notification_delay_ms);
- if (event == ACPI_BATTERY_NOTIFY_INFO)
- acpi_battery_refresh(battery);
+
+ for (i = 0; i < count; i++) {
+ if (events[i] == ACPI_BATTERY_NOTIFY_INFO) {
+ acpi_battery_refresh(battery);
+ break;
+ }
+ }
+
acpi_battery_update(battery, false);
- acpi_bus_generate_netlink_event(ACPI_BATTERY_CLASS,
- dev_name(&device->dev), event,
- acpi_battery_present(battery));
- acpi_notifier_call_chain(ACPI_BATTERY_CLASS, acpi_device_bid(device),
- event, acpi_battery_present(battery));
+
+ for (i = 0; i < count; i++) {
+ acpi_bus_generate_netlink_event(ACPI_BATTERY_CLASS,
+ dev_name(&device->dev), events[i],
+ acpi_battery_present(battery));
+ acpi_notifier_call_chain(ACPI_BATTERY_CLASS, acpi_device_bid(device),
+ events[i], acpi_battery_present(battery));
+ }
+
/* acpi_battery_update could remove power_supply object */
if (old && battery->bat)
power_supply_changed(battery->bat);
}
+static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_battery *battery = data;
+
+ guard(mutex)(&battery->update_lock);
+
+ if (kfifo_avail(&battery->acpi_notif_fifo) >= sizeof(event)) {
+ kfifo_in(&battery->acpi_notif_fifo, &event, sizeof(event));
+ schedule_delayed_work(&battery->acpi_notif_dwork,
+ msecs_to_jiffies(NOTIF_MERGING_MS));
+
+ return;
+ }
+
+ pr_err_ratelimited("too many battery notifications within %dms\n", NOTIF_MERGING_MS);
+}
+
static int battery_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
@@ -1231,6 +1295,29 @@ static int devm_acpi_battery_update_retry(struct device *dev,
return ret;
}
+static void acpi_battery_notify_dwork_cleanup(void *data)
+{
+ struct acpi_battery *battery = data;
+
+ cancel_delayed_work_sync(&battery->acpi_notif_dwork);
+ kfifo_free(&battery->acpi_notif_fifo);
+}
+
+static int devm_acpi_battery_init_notify_dwork(struct device *dev,
+ struct acpi_battery *battery)
+{
+ int ret;
+
+ INIT_DELAYED_WORK(&battery->acpi_notif_dwork, acpi_battery_notification_worker);
+
+ ret = kfifo_alloc(&battery->acpi_notif_fifo,
+ MAX_QUEUED_EVENTS * sizeof(u32), GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, acpi_battery_notify_dwork_cleanup, battery);
+}
+
static int acpi_battery_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1272,6 +1359,10 @@ static int acpi_battery_probe(struct platform_device *pdev)
if (result)
return result;
+ result = devm_acpi_battery_init_notify_dwork(dev, battery);
+ if (result)
+ return result;
+
result = devm_acpi_install_notify_handler(dev, ACPI_ALL_NOTIFY,
acpi_battery_notify, battery);
if (result)
diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h
index 97ce3212edf3..e20d6ad9df80 100644
--- a/drivers/acpi/fan.h
+++ b/drivers/acpi/fan.h
@@ -10,20 +10,21 @@
#ifndef _ACPI_FAN_H_
#define _ACPI_FAN_H_
-#include <linux/kconfig.h>
+#include <linux/device.h>
#include <linux/limits.h>
+#include <linux/types.h>
-#define ACPI_FAN_DEVICE_IDS \
- {"INT3404", }, /* Fan */ \
- {"INTC1044", }, /* Fan for Tiger Lake generation */ \
- {"INTC1048", }, /* Fan for Alder Lake generation */ \
- {"INTC1063", }, /* Fan for Meteor Lake generation */ \
- {"INTC106A", }, /* Fan for Lunar Lake generation */ \
- {"INTC10A2", }, /* Fan for Raptor Lake generation */ \
- {"INTC10D6", }, /* Fan for Panther Lake generation */ \
- {"INTC10FE", }, /* Fan for Wildcat Lake generation */ \
- {"INTC10F5", }, /* Fan for Nova Lake generation */ \
- {"PNP0C0B", } /* Generic ACPI fan */
+#define ACPI_FAN_DEVICE_IDS \
+ { .id = "INT3404" }, /* Fan */ \
+ { .id = "INTC1044" }, /* Fan for Tiger Lake generation */ \
+ { .id = "INTC1048" }, /* Fan for Alder Lake generation */ \
+ { .id = "INTC1063" }, /* Fan for Meteor Lake generation */ \
+ { .id = "INTC106A" }, /* Fan for Lunar Lake generation */ \
+ { .id = "INTC10A2" }, /* Fan for Raptor Lake generation */ \
+ { .id = "INTC10D6" }, /* Fan for Panther Lake generation */ \
+ { .id = "INTC10FE" }, /* Fan for Wildcat Lake generation */ \
+ { .id = "INTC10F5" }, /* Fan for Nova Lake generation */ \
+ { .id = "PNP0C0B" } /* Generic ACPI fan */
#define ACPI_FPS_NAME_LEN 20
@@ -69,7 +70,7 @@ struct acpi_fan {
/**
* acpi_fan_speed_valid - Check if fan speed value is valid
- * @speeed: Speed value returned by the ACPI firmware
+ * @speed: Speed value returned by the ACPI firmware
*
* Check if the fan speed value returned by the ACPI firmware is valid. This function is
* necessary as ACPI firmware implementations can return 0xFFFFFFFF to signal that the
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 2bbe1fae2c52..e9e374f2ad66 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1765,7 +1765,6 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
* Some ACPI devs contain SerialBus resources even though they are not
* attached to a serial bus at all.
*/
- {ACPI_VIDEO_HID, },
{"MSHW0028", },
/*
* HIDs of device with an UartSerialBusV2 resource for which userspace
@@ -1788,6 +1787,9 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
fwnode_property_present(&device->fwnode, "baud")))
return true;
+ if (acpi_dev_is_video_device(device))
+ return false;
+
if (!acpi_match_device_ids(device, ignore_serial_bus_ids))
return false;
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 6ab27e4826d1..d499b72574ab 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -1048,6 +1048,23 @@ static int __init acpi_backlight(char *str)
__setup("acpi_backlight=", acpi_backlight);
/**
+ * acpi_dev_is_video_device - test if device matches against ACPI video device IDs
+ * @adev: ACPI device to test
+ *
+ * Return: true when matches, otherwise false.
+ */
+bool acpi_dev_is_video_device(struct acpi_device *adev)
+{
+ static const struct acpi_device_id video_device_ids[] = {
+ { .id = ACPI_VIDEO_HID },
+ { }
+ };
+
+ return adev && !acpi_match_device_ids(adev, video_device_ids);
+}
+EXPORT_SYMBOL(acpi_dev_is_video_device);
+
+/**
* acpi_match_platform_list - Check if the system matches with a given list
* @plat: pointer to acpi_platform_list table terminated by a NULL entry
*
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index c5b08c19e847..5b0dc679e322 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -67,12 +67,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
long *cap = context;
struct pci_dev *dev;
- static const struct acpi_device_id video_ids[] = {
- {ACPI_VIDEO_HID, 0},
- {"", 0},
- };
-
- if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) {
+ if (acpi_dev_is_video_device(acpi_dev)) {
dev = acpi_dev_get_pci_dev(acpi_dev);
if (!dev)
return AE_OK;
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index e5fddacae9a4..42a7e777346b 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -131,15 +131,6 @@ static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data)
return 1;
}
-static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
- /*
- * ACPI video acpi_devices, which are handled by the acpi-video driver
- * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these.
- */
- { ACPI_VIDEO_HID, 0 },
- {}
-};
-
struct i2c_acpi_irq_context {
int irq;
bool wake_capable;
@@ -158,7 +149,11 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev,
if (!acpi_dev_ready_for_enumeration(adev))
return -ENODEV;
- if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0)
+ /*
+ * ACPI video devices, which are handled by the acpi-video driver,
+ * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these.
+ */
+ if (acpi_dev_is_video_device(adev))
return -ENODEV;
memset(info, 0, sizeof(*info));
diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c
index c360eee11dd9..3de05aee7859 100644
--- a/drivers/pci/vgaarb.c
+++ b/drivers/pci/vgaarb.c
@@ -575,9 +575,7 @@ static bool vga_is_firmware_default(struct pci_dev *pdev)
static bool vga_arb_integrated_gpu(struct device *dev)
{
#if defined(CONFIG_ACPI)
- struct acpi_device *adev = ACPI_COMPANION(dev);
-
- return adev && !strcmp(acpi_device_hid(adev), ACPI_VIDEO_HID);
+ return acpi_dev_is_video_device(ACPI_COMPANION(dev));
#else
return false;
#endif
diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
index 445e1403308e..b528a1e67dd3 100644
--- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
+++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
@@ -768,7 +768,7 @@ static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle,
if (!strcmp(context, "video")) {
struct acpi_device *dev = acpi_fetch_acpi_dev(handle);
- if (!dev || strcmp(ACPI_VIDEO_HID, acpi_device_hid(dev)))
+ if (!acpi_dev_is_video_device(dev))
return AE_OK;
}
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 8d20a25f1a62..ddacac812094 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -470,6 +470,7 @@ extern char *wmi_get_acpi_device_uid(const char *guid);
#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800
extern char acpi_video_backlight_string[];
+extern bool acpi_dev_is_video_device(struct acpi_device *adev);
extern long acpi_is_video_device(acpi_handle handle);
extern void acpi_osi_setup(char *str);