From 4352b8aee98005853aa63f57d6377282de17a33f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 23 Aug 2026 13:28:57 -0700 Subject: Merge tag 'i3c/for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull I3C updates from Alexandre Belloni: "A new driver this cycle, for the AMD AXI I3C controller. There are also new features such as IBI wakeup support, SETAASA device discovery and ACPI support for the the DAA methods, meaning we can now communicate with the SPD devices on DDR5 modules. New driver: - AMD AXI I3C master controller Subsystem: - I3C Common Command Code (CCC) handling improvements, especially around GET CCCs - SETAASA device discovery support - ACPI support for all existing DAA methods like SETDASA, SETNEWDA as well as I2C devices on I3C bus - IBI-based wakeup support Drivers: - dw: SETAASA support - mipi-i3c-hci: advertise IBI wakeup capability, AMD_PT I3C controller support, PIO queue management support for HCI v1.2 - renesas: improve suspend to RAM support, add runtime PM support" * tag 'i3c/for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (70 commits) i3c: dw: reduce do_daa time if there's no client i3c: mipi-i3c-hci: Add support for AMD_PT I3C controller i3c: mipi-i3c-hci: Add PIO queue management support for HCI v1.2 i3c: mipi-i3c-hci: Fix missing STAT_IBI_STATUS_THLD in PIO mode i3c: dw: rename "pclk" to "apb" to match dt-binding i3c: dw: make struct dw_i3c_cmd smaller i3c: dw: use COMMAND_PORT_TRANSFER_ARG instead of hardcoding i3c: renesas: Don't register devices when ENTDAA times out i3c: master: dw-i3c-master: fix OD timing for first broadcast i3c: mipi-i3c-hci: Advertise IBI wakeup capability i3c: mipi-i3c-hci: Factor out i3c_hci_sysdev() i3c: mipi-i3c-hci-pci: Propagate I3C wakeup requirements to PCI i3c: master: Reject IBI requests from non-IBI-capable devices i3c: master: Add helper to query bus wakeup requirements i3c: master: Report wakeup events for IBIs i3c: master: Support IBI-based wakeup capability i3c: master: Fix potential UAF in i3c_device_match() i3c: master: Fix potential UAF in i3c_device_uevent() i3c: Make dev->desc locking assumptions explicit i3c: master: Fix use-after-free of master->this ... --- drivers/acpi/dptf/Kconfig | 46 +++++++ drivers/acpi/dptf/Makefile | 3 + drivers/acpi/dptf/dptf_pch_fivr.c | 173 +++++++++++++++++++++++++ drivers/acpi/dptf/dptf_power.c | 262 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 484 insertions(+) create mode 100644 drivers/acpi/dptf/Kconfig create mode 100644 drivers/acpi/dptf/Makefile create mode 100644 drivers/acpi/dptf/dptf_pch_fivr.c create mode 100644 drivers/acpi/dptf/dptf_power.c (limited to 'drivers/acpi/dptf') diff --git a/drivers/acpi/dptf/Kconfig b/drivers/acpi/dptf/Kconfig new file mode 100644 index 000000000..4b3fdc03e --- /dev/null +++ b/drivers/acpi/dptf/Kconfig @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0 + +menuconfig ACPI_DPTF + bool "Intel DPTF (Dynamic Platform and Thermal Framework) Support" + depends on X86 + help + Intel Dynamic Platform and Thermal Framework (DPTF) is a platform + level hardware/software solution for power and thermal management. + + As a container for multiple power/thermal technologies, DPTF provides + a coordinated approach for different policies to effect the hardware + state of a system. + +if ACPI_DPTF + +config DPTF_POWER + tristate "Platform Power DPTF Participant" + default m + help + This driver adds support for Dynamic Platform and Thermal Framework + (DPTF) Platform Power Participant device (INT3407) support. + This participant is responsible for exposing platform telemetry: + max_platform_power + platform_power_source + adapter_rating + battery_steady_power + charger_type + + To compile this driver as a module, choose M here: + the module will be called dptf_power. + +config DPTF_PCH_FIVR + tristate "PCH FIVR DPTF Participant" + default m + help + This driver adds support for Dynamic Platform and Thermal Framework + (DPTF) PCH FIVR Participant device support. This driver allows to + switch the PCH FIVR (Fully Integrated Voltage Regulator) frequency. + This participant is responsible for exposing: + freq_mhz_low_clock + freq_mhz_high_clock + + To compile this driver as a module, choose M here: + the module will be called dptf_pch_fivr. + +endif diff --git a/drivers/acpi/dptf/Makefile b/drivers/acpi/dptf/Makefile new file mode 100644 index 000000000..e912a3be1 --- /dev/null +++ b/drivers/acpi/dptf/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DPTF_POWER) += dptf_power.o +obj-$(CONFIG_DPTF_PCH_FIVR) += dptf_pch_fivr.o diff --git a/drivers/acpi/dptf/dptf_pch_fivr.c b/drivers/acpi/dptf/dptf_pch_fivr.c new file mode 100644 index 000000000..8d7e55592 --- /dev/null +++ b/drivers/acpi/dptf/dptf_pch_fivr.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * dptf_pch_fivr: DPTF PCH FIVR Participant driver + * Copyright (c) 2020, Intel Corporation. + */ + +#include +#include +#include +#include + +struct pch_fivr_resp { + u64 status; + u64 result; +}; + +static int pch_fivr_read(acpi_handle handle, char *method, struct pch_fivr_resp *fivr_resp) +{ + struct acpi_buffer resp = { sizeof(struct pch_fivr_resp), fivr_resp}; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer format = { sizeof("NN"), "NN" }; + union acpi_object *obj; + acpi_status status; + int ret = -EFAULT; + + status = acpi_evaluate_object(handle, method, NULL, &buffer); + if (ACPI_FAILURE(status)) + return ret; + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE) + goto release_buffer; + + status = acpi_extract_package(obj, &format, &resp); + if (ACPI_FAILURE(status)) + goto release_buffer; + + if (fivr_resp->status) + goto release_buffer; + + ret = 0; + +release_buffer: + ACPI_FREE(buffer.pointer); + return ret; +} + +/* + * Presentation of attributes which are defined for INTC10xx + * They are: + * freq_mhz_low_clock : Set PCH FIVR switching freq for + * FIVR clock 19.2MHz and 24MHz + * freq_mhz_high_clock : Set PCH FIVR switching freq for + * FIVR clock 38.4MHz + */ +#define PCH_FIVR_SHOW(name, method) \ +static ssize_t name##_show(struct device *dev,\ + struct device_attribute *attr,\ + char *buf)\ +{\ + struct acpi_device *acpi_dev = dev_get_drvdata(dev);\ + struct pch_fivr_resp fivr_resp;\ + int status;\ +\ + status = pch_fivr_read(acpi_dev->handle, #method, &fivr_resp);\ + if (status)\ + return status;\ +\ + return sprintf(buf, "%llu\n", fivr_resp.result);\ +} + +#define PCH_FIVR_STORE(name, method) \ +static ssize_t name##_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;\ + u32 val;\ +\ + if (kstrtouint(buf, 0, &val) < 0)\ + return -EINVAL;\ +\ + status = acpi_execute_simple_method(acpi_dev->handle, #method, val);\ + if (ACPI_SUCCESS(status))\ + return count;\ +\ + return -EINVAL;\ +} + +PCH_FIVR_SHOW(freq_mhz_low_clock, GFC0) +PCH_FIVR_SHOW(freq_mhz_high_clock, GFC1) +PCH_FIVR_SHOW(ssc_clock_info, GEMI) +PCH_FIVR_SHOW(fivr_switching_freq_mhz, GFCS) +PCH_FIVR_SHOW(fivr_switching_fault_status, GFFS) +PCH_FIVR_STORE(freq_mhz_low_clock, RFC0) +PCH_FIVR_STORE(freq_mhz_high_clock, RFC1) + +static DEVICE_ATTR_RW(freq_mhz_low_clock); +static DEVICE_ATTR_RW(freq_mhz_high_clock); +static DEVICE_ATTR_RO(ssc_clock_info); +static DEVICE_ATTR_RO(fivr_switching_freq_mhz); +static DEVICE_ATTR_RO(fivr_switching_fault_status); + +static struct attribute *fivr_attrs[] = { + &dev_attr_freq_mhz_low_clock.attr, + &dev_attr_freq_mhz_high_clock.attr, + &dev_attr_ssc_clock_info.attr, + &dev_attr_fivr_switching_freq_mhz.attr, + &dev_attr_fivr_switching_fault_status.attr, + NULL +}; + +static const struct attribute_group pch_fivr_attribute_group = { + .attrs = fivr_attrs, + .name = "pch_fivr_switch_frequency" +}; + +static int pch_fivr_add(struct platform_device *pdev) +{ + struct acpi_device *acpi_dev; + unsigned long long ptype; + acpi_status status; + int result; + + acpi_dev = ACPI_COMPANION(&(pdev->dev)); + if (!acpi_dev) + return -ENODEV; + + status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype); + if (ACPI_FAILURE(status) || ptype != 0x05) + return -ENODEV; + + result = sysfs_create_group(&pdev->dev.kobj, + &pch_fivr_attribute_group); + if (result) + return result; + + platform_set_drvdata(pdev, acpi_dev); + + return 0; +} + +static void pch_fivr_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &pch_fivr_attribute_group); +} + +static const struct acpi_device_id pch_fivr_device_ids[] = { + {"INTC1045", 0}, + {"INTC1049", 0}, + {"INTC1064", 0}, + {"INTC106B", 0}, + {"INTC10A3", 0}, + {"INTC10D7", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, pch_fivr_device_ids); + +static struct platform_driver pch_fivr_driver = { + .probe = pch_fivr_add, + .remove = pch_fivr_remove, + .driver = { + .name = "dptf_pch_fivr", + .acpi_match_table = pch_fivr_device_ids, + }, +}; + +module_platform_driver(pch_fivr_driver); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ACPI DPTF PCH FIVR driver"); diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c new file mode 100644 index 000000000..55ccbb8dd --- /dev/null +++ b/drivers/acpi/dptf/dptf_power.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * dptf_power: DPTF platform power driver + * Copyright (c) 2016, Intel Corporation. + */ + +#include +#include +#include +#include + +/* + * Presentation of attributes which are defined for INT3407 and INT3532. + * They are: + * PMAX : Maximum platform power + * PSRC : Platform power source + * ARTG : Adapter rating + * CTYP : Charger type + * PROP : Rest of worst case platform Power + * 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,\ + struct device_attribute *attr,\ + char *buf)\ +{\ + struct acpi_device *acpi_dev = dev_get_drvdata(dev);\ + unsigned long long val;\ + acpi_status status;\ +\ + status = acpi_evaluate_integer(acpi_dev->handle, #object,\ + NULL, &val);\ + if (ACPI_SUCCESS(status))\ + return sprintf(buf, "%d\n", (int)val);\ + else \ + return -EINVAL;\ +} + +DPTF_POWER_SHOW(max_platform_power_mw, PMAX) +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, + &dev_attr_platform_power_source.attr, + &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 +}; + +static const struct attribute_group dptf_power_attribute_group = { + .attrs = dptf_power_attrs, + .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 IMPEDANCE_CHANGED 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 IMPEDANCE_CHANGED: + attr = "high_freq_impedance_mohm"; + 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; + unsigned long long ptype; + int result; + + acpi_dev = ACPI_COMPANION(&(pdev->dev)); + if (!acpi_dev) + return -ENODEV; + + 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; + + 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, + 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); + + return 0; +} + +static void 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); +} + +static const struct acpi_device_id int3407_device_ids[] = { + {"INT3407", 0}, + {"INT3532", 0}, + {"INTC1047", 0}, + {"INTC1050", 0}, + {"INTC1060", 0}, + {"INTC1061", 0}, + {"INTC1065", 0}, + {"INTC1066", 0}, + {"INTC106C", 0}, + {"INTC106D", 0}, + {"INTC10A4", 0}, + {"INTC10A5", 0}, + {"INTC10D8", 0}, + {"INTC10D9", 0}, + {"INTC1100", 0}, + {"INTC1101", 0}, + {"INTC10F7", 0}, + {"INTC10F8", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, int3407_device_ids); + +static struct platform_driver dptf_power_driver = { + .probe = dptf_power_add, + .remove = dptf_power_remove, + .driver = { + .name = "dptf_power", + .acpi_match_table = int3407_device_ids, + }, +}; + +module_platform_driver(dptf_power_driver); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ACPI DPTF platform power driver"); -- cgit v1.3.1