Commit 48ccdedd authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'acpi-apei', 'acpi-pmic', 'acpi-video' and 'acpi-dptf'

* acpi-apei:
  arm64: acpi: Make apei_claim_sea() synchronise with APEI's irq work
  ACPI: APEI: Kick the memory_failure() queue for synchronous errors
  mm/memory-failure: Add memory_failure_queue_kick()

* acpi-pmic:
  ACPI / PMIC: Add i2c address for thermal control

* acpi-video:
  ACPI: video: Use native backlight on Acer TravelMate 5735Z

* acpi-dptf:
  ACPI: DPTF: Add battery participant driver
  ACPI: DPTF: Additional sysfs attributes for power participant driver
Loading
Loading
Loading
Loading
+58 −4
Original line number Diff line number Diff line
@@ -27,10 +27,12 @@ KernelVersion: v4.10
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) Display the platform power source
		bits[3:0] Current power source
			0x00 = DC
			0x01 = AC
			0x02 = USB
			0x03 = Wireless Charger
		bits[7:4] Power source sequence number

What:		/sys/bus/platform/devices/INT3407:00/dptf_power/battery_steady_power
Date:		Jul, 2016
@@ -38,3 +40,55 @@ KernelVersion: v4.10
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) The maximum sustained power for battery in milliwatts.

What:		/sys/bus/platform/devices/INT3407:00/dptf_power/rest_of_platform_power_mw
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) Shows the rest (outside of SoC) of worst-case platform power.

What:		/sys/bus/platform/devices/INT3407:00/dptf_power/prochot_confirm
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(WO) Confirm embedded controller about a prochot notification.

What:		/sys/bus/platform/devices/INT3532:00/dptf_battery/max_platform_power_mw
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) The maximum platform power that can be supported by the battery in milli watts.

What:		/sys/bus/platform/devices/INT3532:00/dptf_battery/max_steady_state_power_mw
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) The maximum sustained power for battery in milli watts.

What:		/sys/bus/platform/devices/INT3532:00/dptf_battery/high_freq_impedance_mohm
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) The high frequency impedance value that can be obtained from battery
		fuel gauge in milli Ohms.

What:		/sys/bus/platform/devices/INT3532:00/dptf_battery/no_load_voltage_mv
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) The no-load voltage that can be obtained from battery fuel gauge in
		milli volts.

What:		/sys/bus/platform/devices/INT3532:00/dptf_battery/current_discharge_capbility_ma
Date:		June, 2020
KernelVersion:	v5.8
Contact:	linux-acpi@vger.kernel.org
Description:
		(RO) The battery discharge current capability obtained from battery fuel gauge in
		milli Amps.
+25 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/irq_work.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/smp.h>
@@ -269,6 +270,7 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)
int apei_claim_sea(struct pt_regs *regs)
{
	int err = -ENOENT;
	bool return_to_irqs_enabled;
	unsigned long current_flags;

	if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
@@ -276,6 +278,12 @@ int apei_claim_sea(struct pt_regs *regs)

	current_flags = local_daif_save_flags();

	/* current_flags isn't useful here as daif doesn't tell us about pNMI */
	return_to_irqs_enabled = !irqs_disabled_flags(arch_local_save_flags());

	if (regs)
		return_to_irqs_enabled = interrupts_enabled(regs);

	/*
	 * SEA can interrupt SError, mask it and describe this as an NMI so
	 * that APEI defers the handling.
@@ -284,6 +292,23 @@ int apei_claim_sea(struct pt_regs *regs)
	nmi_enter();
	err = ghes_notify_sea();
	nmi_exit();

	/*
	 * APEI NMI-like notifications are deferred to irq_work. Unless
	 * we interrupted irqs-masked code, we can do that now.
	 */
	if (!err) {
		if (return_to_irqs_enabled) {
			local_daif_restore(DAIF_PROCCTX_NOIRQ);
			__irq_enter();
			irq_work_run();
			__irq_exit();
		} else {
			pr_warn_ratelimited("APEI work queued but not completed");
			err = -EINPROGRESS;
		}
	}

	local_daif_restore(current_flags);

	return err;
+7 −5
Original line number Diff line number Diff line
@@ -635,11 +635,13 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)

	inf = esr_to_fault_info(esr);

	if (user_mode(regs) && apei_claim_sea(regs) == 0) {
		/*
	 * Return value ignored as we rely on signal merging.
	 * Future patches will make this more robust.
		 * APEI claimed this as a firmware-first notification.
		 * Some processing deferred to task_work before ret_to_user().
		 */
	apei_claim_sea(regs);
		return 0;
	}

	if (esr & ESR_ELx_FnV)
		siaddr = NULL;
+56 −11
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include <linux/sched/clock.h>
#include <linux/uuid.h>
#include <linux/ras.h>
#include <linux/task_work.h>

#include <acpi/actbl1.h>
#include <acpi/ghes.h>
@@ -414,23 +415,46 @@ static void ghes_clear_estatus(struct ghes *ghes,
		ghes_ack_error(ghes->generic_v2);
}

static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev)
/*
 * Called as task_work before returning to user-space.
 * Ensure any queued work has been done before we return to the context that
 * triggered the notification.
 */
static void ghes_kick_task_work(struct callback_head *head)
{
	struct acpi_hest_generic_status *estatus;
	struct ghes_estatus_node *estatus_node;
	u32 node_len;

	estatus_node = container_of(head, struct ghes_estatus_node, task_work);
	if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE))
		memory_failure_queue_kick(estatus_node->task_work_cpu);

	estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
	node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus));
	gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len);
}

static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
				       int sev)
{
#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
	unsigned long pfn;
	int flags = -1;
	int sec_sev = ghes_severity(gdata->error_severity);
	struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);

	if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE))
		return false;

	if (!(mem_err->validation_bits & CPER_MEM_VALID_PA))
		return;
		return false;

	pfn = mem_err->physical_addr >> PAGE_SHIFT;
	if (!pfn_valid(pfn)) {
		pr_warn_ratelimited(FW_WARN GHES_PFX
		"Invalid address in generic error data: %#llx\n",
		mem_err->physical_addr);
		return;
		return false;
	}

	/* iff following two events can be handled properly by now */
@@ -440,9 +464,12 @@ static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int
	if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
		flags = 0;

	if (flags != -1)
	if (flags != -1) {
		memory_failure_queue(pfn, flags);
#endif
		return true;
	}

	return false;
}

/*
@@ -490,7 +517,7 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
#endif
}

static void ghes_do_proc(struct ghes *ghes,
static bool ghes_do_proc(struct ghes *ghes,
			 const struct acpi_hest_generic_status *estatus)
{
	int sev, sec_sev;
@@ -498,6 +525,7 @@ static void ghes_do_proc(struct ghes *ghes,
	guid_t *sec_type;
	const guid_t *fru_id = &guid_null;
	char *fru_text = "";
	bool queued = false;

	sev = ghes_severity(estatus->error_severity);
	apei_estatus_for_each_section(estatus, gdata) {
@@ -515,7 +543,7 @@ static void ghes_do_proc(struct ghes *ghes,
			ghes_edac_report_mem_error(sev, mem_err);

			arch_apei_report_mem_error(sev, mem_err);
			ghes_handle_memory_failure(gdata, sev);
			queued = ghes_handle_memory_failure(gdata, sev);
		}
		else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
			ghes_handle_aer(gdata);
@@ -532,6 +560,8 @@ static void ghes_do_proc(struct ghes *ghes,
					       gdata->error_data_length);
		}
	}

	return queued;
}

static void __ghes_print_estatus(const char *pfx,
@@ -827,7 +857,9 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
	struct ghes_estatus_node *estatus_node;
	struct acpi_hest_generic *generic;
	struct acpi_hest_generic_status *estatus;
	bool task_work_pending;
	u32 len, node_len;
	int ret;

	llnode = llist_del_all(&ghes_estatus_llist);
	/*
@@ -842,14 +874,26 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
		estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
		len = cper_estatus_len(estatus);
		node_len = GHES_ESTATUS_NODE_LEN(len);
		ghes_do_proc(estatus_node->ghes, estatus);
		task_work_pending = ghes_do_proc(estatus_node->ghes, estatus);
		if (!ghes_estatus_cached(estatus)) {
			generic = estatus_node->generic;
			if (ghes_print_estatus(NULL, generic, estatus))
				ghes_estatus_cache_add(generic, estatus);
		}
		gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
			      node_len);

		if (task_work_pending && current->mm != &init_mm) {
			estatus_node->task_work.func = ghes_kick_task_work;
			estatus_node->task_work_cpu = smp_processor_id();
			ret = task_work_add(current, &estatus_node->task_work,
					    true);
			if (ret)
				estatus_node->task_work.func = NULL;
		}

		if (!estatus_node->task_work.func)
			gen_pool_free(ghes_estatus_pool,
				      (unsigned long)estatus_node, node_len);

		llnode = next;
	}
}
@@ -909,6 +953,7 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,

	estatus_node->ghes = ghes;
	estatus_node->generic = ghes->generic;
	estatus_node->task_work.func = NULL;
	estatus = GHES_ESTATUS_FROM_NODE(estatus_node);

	if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) {
+138 −9
Original line number Diff line number Diff line
@@ -10,12 +10,19 @@
#include <linux/platform_device.h>

/*
 * Presentation of attributes which are defined for INT3407. They are:
 * Presentation of attributes which are defined for INT3407 and INT3532.
 * They are:
 * PMAX : Maximum platform powe
 * PSRC : Platform power source
 * ARTG : Adapter rating
 * CTYP : Charger type
 * PBSS : Battery steady power
 * PROP : Rest of worst case platform Power
 * PBSS : Power Battery Steady State
 * PBSS : Power Battery Steady State
 * RBHF : High Frequency Impedance
 * VBNL : Instantaneous No-Load Voltage
 * CMPP : Current Discharge Capability
 */
#define DPTF_POWER_SHOW(name, object) \
static ssize_t name##_show(struct device *dev,\
@@ -39,12 +46,42 @@ DPTF_POWER_SHOW(platform_power_source, PSRC)
DPTF_POWER_SHOW(adapter_rating_mw, ARTG)
DPTF_POWER_SHOW(battery_steady_power_mw, PBSS)
DPTF_POWER_SHOW(charger_type, CTYP)
DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP)
DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS)
DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF)
DPTF_POWER_SHOW(no_load_voltage_mv, VBNL)
DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP);

static DEVICE_ATTR_RO(max_platform_power_mw);
static DEVICE_ATTR_RO(platform_power_source);
static DEVICE_ATTR_RO(adapter_rating_mw);
static DEVICE_ATTR_RO(battery_steady_power_mw);
static DEVICE_ATTR_RO(charger_type);
static DEVICE_ATTR_RO(rest_of_platform_power_mw);
static DEVICE_ATTR_RO(max_steady_state_power_mw);
static DEVICE_ATTR_RO(high_freq_impedance_mohm);
static DEVICE_ATTR_RO(no_load_voltage_mv);
static DEVICE_ATTR_RO(current_discharge_capbility_ma);

static ssize_t prochot_confirm_store(struct device *dev,
				     struct device_attribute *attr,
				     const char *buf, size_t count)
{
	struct acpi_device *acpi_dev = dev_get_drvdata(dev);
	acpi_status status;
	int seq_no;

	if (kstrtouint(buf, 0, &seq_no) < 0)
		return -EINVAL;

	status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no);
	if (ACPI_SUCCESS(status))
		return count;

	return -EINVAL;
}

static DEVICE_ATTR_WO(prochot_confirm);

static struct attribute *dptf_power_attrs[] = {
	&dev_attr_max_platform_power_mw.attr,
@@ -52,6 +89,8 @@ static struct attribute *dptf_power_attrs[] = {
	&dev_attr_adapter_rating_mw.attr,
	&dev_attr_battery_steady_power_mw.attr,
	&dev_attr_charger_type.attr,
	&dev_attr_rest_of_platform_power_mw.attr,
	&dev_attr_prochot_confirm.attr,
	NULL
};

@@ -60,10 +99,79 @@ static const struct attribute_group dptf_power_attribute_group = {
	.name = "dptf_power"
};

static struct attribute *dptf_battery_attrs[] = {
	&dev_attr_max_platform_power_mw.attr,
	&dev_attr_max_steady_state_power_mw.attr,
	&dev_attr_high_freq_impedance_mohm.attr,
	&dev_attr_no_load_voltage_mv.attr,
	&dev_attr_current_discharge_capbility_ma.attr,
	NULL
};

static const struct attribute_group dptf_battery_attribute_group = {
	.attrs = dptf_battery_attrs,
	.name = "dptf_battery"
};

#define MAX_POWER_CHANGED		0x80
#define POWER_STATE_CHANGED		0x81
#define STEADY_STATE_POWER_CHANGED	0x83
#define POWER_PROP_CHANGE_EVENT	0x84
#define IMPEDANCED_CHNGED		0x85
#define VOLTAGE_CURRENT_CHANGED	0x86

static long long dptf_participant_type(acpi_handle handle)
{
	unsigned long long ptype;
	acpi_status status;

	status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	return ptype;
}

static void dptf_power_notify(acpi_handle handle, u32 event, void *data)
{
	struct platform_device *pdev = data;
	char *attr;

	switch (event) {
	case POWER_STATE_CHANGED:
		attr = "platform_power_source";
		break;
	case POWER_PROP_CHANGE_EVENT:
		attr = "rest_of_platform_power_mw";
		break;
	case MAX_POWER_CHANGED:
		attr = "max_platform_power_mw";
		break;
	case STEADY_STATE_POWER_CHANGED:
		attr = "max_steady_state_power_mw";
		break;
	case VOLTAGE_CURRENT_CHANGED:
		attr = "no_load_voltage_mv";
		break;
	default:
		dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event);
		return;
	}

	/*
	 * Notify that an attribute is changed, so that user space can read
	 * again.
	 */
	if (dptf_participant_type(handle) == 0x0CULL)
		sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr);
	else
		sysfs_notify(&pdev->dev.kobj, "dptf_power", attr);
}

static int dptf_power_add(struct platform_device *pdev)
{
	const struct attribute_group *attr_group;
	struct acpi_device *acpi_dev;
	acpi_status status;
	unsigned long long ptype;
	int result;

@@ -71,17 +179,29 @@ static int dptf_power_add(struct platform_device *pdev)
	if (!acpi_dev)
		return -ENODEV;

	status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype);
	if (ACPI_FAILURE(status))
	ptype = dptf_participant_type(acpi_dev->handle);
	if (ptype == 0x11)
		attr_group = &dptf_power_attribute_group;
	else if (ptype == 0x0C)
		attr_group = &dptf_battery_attribute_group;
	else
		return -ENODEV;

	if (ptype != 0x11)
		return -ENODEV;
	result = acpi_install_notify_handler(acpi_dev->handle,
					     ACPI_DEVICE_NOTIFY,
					     dptf_power_notify,
					     (void *)pdev);
	if (result)
		return result;

	result = sysfs_create_group(&pdev->dev.kobj,
				    &dptf_power_attribute_group);
	if (result)
				    attr_group);
	if (result) {
		acpi_remove_notify_handler(acpi_dev->handle,
					   ACPI_DEVICE_NOTIFY,
					   dptf_power_notify);
		return result;
	}

	platform_set_drvdata(pdev, acpi_dev);

@@ -90,7 +210,15 @@ static int dptf_power_add(struct platform_device *pdev)

static int dptf_power_remove(struct platform_device *pdev)
{
	struct acpi_device *acpi_dev = platform_get_drvdata(pdev);

	acpi_remove_notify_handler(acpi_dev->handle,
				   ACPI_DEVICE_NOTIFY,
				   dptf_power_notify);

	if (dptf_participant_type(acpi_dev->handle) == 0x0CULL)
		sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group);
	else
		sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group);

	return 0;
@@ -98,6 +226,7 @@ static int dptf_power_remove(struct platform_device *pdev)

static const struct acpi_device_id int3407_device_ids[] = {
	{"INT3407", 0},
	{"INT3532", 0},
	{"INTC1047", 0},
	{"", 0},
};
Loading