diff --git a/Documentation/ABI/testing/sysfs-platform_profile b/Documentation/ABI/testing/sysfs-platform_profile index baf1d125f9f8..125324ab53a9 100644 --- a/Documentation/ABI/testing/sysfs-platform_profile +++ b/Documentation/ABI/testing/sysfs-platform_profile @@ -33,3 +33,8 @@ Description: Reading this file gives the current selected profile for this source such as e.g. a hotkey triggered profile change handled either directly by the embedded-controller or fully handled inside the kernel. + + This file may also emit the string 'custom' to indicate + that multiple platform profiles drivers are in use but + have different values. This string can not be written to + this interface and is solely for informational purposes. diff --git a/Documentation/userspace-api/sysfs-platform_profile.rst b/Documentation/userspace-api/sysfs-platform_profile.rst index 4fccde2e4563..7f013356118a 100644 --- a/Documentation/userspace-api/sysfs-platform_profile.rst +++ b/Documentation/userspace-api/sysfs-platform_profile.rst @@ -40,3 +40,41 @@ added. Drivers which wish to introduce new profile names must: 1. Explain why the existing profile names cannot be used. 2. Add the new profile name, along with a clear description of the expected behaviour, to the sysfs-platform_profile ABI documentation. + +"Custom" profile support +======================== +The platform_profile class also supports profiles advertising a "custom" +profile. This is intended to be set by drivers when the setttings in the +driver have been modified in a way that a standard profile doesn't represent +the current state. + +Multiple driver support +======================= +When multiple drivers on a system advertise a platform profile handler, the +platform profile handler core will only advertise the profiles that are +common between all drivers to the ``/sys/firmware/acpi`` interfaces. + +This is to ensure there is no ambiguity on what the profile names mean when +all handlers don't support a profile. + +Individual drivers will register a 'platform_profile' class device that has +similar semantics as the ``/sys/firmware/acpi/platform_profile`` interface. + +To discover which driver is associated with a platform profile handler the +user can read the ``name`` attribute of the class device. + +To discover available profiles from the class interface the user can read the +``choices`` attribute. + +If a user wants to select a profile for a specific driver, they can do so +by writing to the ``profile`` attribute of the driver's class device. + +This will allow users to set different profiles for different drivers on the +same system. If the selected profile by individual drivers differs the +platform profile handler core will display the profile 'custom' to indicate +that the profiles are not the same. + +While the ``platform_profile`` attribute has the value ``custom``, writing a +common profile from ``platform_profile_choices`` to the platform_profile +attribute of the platform profile handler core will set the profile for all +drivers. diff --git a/MAINTAINERS b/MAINTAINERS index d76b99db1f3e..03d04df7b44c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1133,7 +1133,7 @@ F: include/linux/pds/ AMD PMC DRIVER M: Shyam Sundar S K L: platform-driver-x86@vger.kernel.org -S: Maintained +S: Supported F: drivers/platform/x86/amd/pmc/ AMD PMF DRIVER diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h index 4a7cace06204..92d8f256d096 100644 --- a/arch/x86/include/uapi/asm/amd_hsmp.h +++ b/arch/x86/include/uapi/asm/amd_hsmp.h @@ -50,6 +50,12 @@ enum hsmp_message_ids { HSMP_GET_METRIC_TABLE_VER, /* 23h Get metrics table version */ HSMP_GET_METRIC_TABLE, /* 24h Get metrics table */ HSMP_GET_METRIC_TABLE_DRAM_ADDR,/* 25h Get metrics table dram address */ + HSMP_SET_XGMI_PSTATE_RANGE, /* 26h Set xGMI P-state range */ + HSMP_CPU_RAIL_ISO_FREQ_POLICY, /* 27h Get/Set Cpu Iso frequency policy */ + HSMP_DFC_ENABLE_CTRL, /* 28h Enable/Disable DF C-state */ + HSMP_GET_RAPL_UNITS = 0x30, /* 30h Get scaling factor for energy */ + HSMP_GET_RAPL_CORE_COUNTER, /* 31h Get core energy counter value */ + HSMP_GET_RAPL_PACKAGE_COUNTER, /* 32h Get package energy counter value */ HSMP_MSG_ID_MAX, }; @@ -65,6 +71,7 @@ enum hsmp_msg_type { HSMP_RSVD = -1, HSMP_SET = 0, HSMP_GET = 1, + HSMP_SET_GET = 2, }; enum hsmp_proto_versions { @@ -72,7 +79,8 @@ enum hsmp_proto_versions { HSMP_PROTO_VER3, HSMP_PROTO_VER4, HSMP_PROTO_VER5, - HSMP_PROTO_VER6 + HSMP_PROTO_VER6, + HSMP_PROTO_VER7 }; struct hsmp_msg_desc { @@ -300,7 +308,7 @@ static const struct hsmp_msg_desc hsmp_msg_desc_table[] * HSMP_SET_POWER_MODE, num_args = 1, response_sz = 0 * input: args[0] = power efficiency mode[2:0] */ - {1, 0, HSMP_SET}, + {1, 1, HSMP_SET_GET}, /* * HSMP_SET_PSTATE_MAX_MIN, num_args = 1, response_sz = 0 @@ -325,6 +333,58 @@ static const struct hsmp_msg_desc hsmp_msg_desc_table[] * output: args[1] = upper 32 bits of the address */ {0, 2, HSMP_GET}, + + /* + * HSMP_SET_XGMI_PSTATE_RANGE, num_args = 1, response_sz = 0 + * input: args[0] = min xGMI p-state[15:8] + max xGMI p-state[7:0] + */ + {1, 0, HSMP_SET}, + + /* + * HSMP_CPU_RAIL_ISO_FREQ_POLICY, num_args = 1, response_sz = 1 + * input: args[0] = set/get policy[31] + + * disable/enable independent control[0] + * output: args[0] = current policy[0] + */ + {1, 1, HSMP_SET_GET}, + + /* + * HSMP_DFC_ENABLE_CTRL, num_args = 1, response_sz = 1 + * input: args[0] = set/get policy[31] + enable/disable DFC[0] + * output: args[0] = current policy[0] + */ + {1, 1, HSMP_SET_GET}, + + /* RESERVED(0x29-0x2f) */ + {0, 0, HSMP_RSVD}, + {0, 0, HSMP_RSVD}, + {0, 0, HSMP_RSVD}, + {0, 0, HSMP_RSVD}, + {0, 0, HSMP_RSVD}, + {0, 0, HSMP_RSVD}, + {0, 0, HSMP_RSVD}, + + /* + * HSMP_GET_RAPL_UNITS, response_sz = 1 + * output: args[0] = tu value[19:16] + esu value[12:8] + */ + {0, 1, HSMP_GET}, + + /* + * HSMP_GET_RAPL_CORE_COUNTER, num_args = 1, response_sz = 1 + * input: args[0] = apic id[15:0] + * output: args[0] = lower 32 bits of energy + * output: args[1] = upper 32 bits of energy + */ + {1, 2, HSMP_GET}, + + /* + * HSMP_GET_RAPL_PACKAGE_COUNTER, num_args = 0, response_sz = 1 + * output: args[0] = lower 32 bits of energy + * output: args[1] = upper 32 bits of energy + */ + {0, 2, HSMP_GET}, + }; /* Metrics table (supported only with proto version 6) */ diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c index d2f7fd7743a1..75a1415190ac 100644 --- a/drivers/acpi/platform_profile.c +++ b/drivers/acpi/platform_profile.c @@ -5,11 +5,11 @@ #include #include #include +#include #include #include #include -static struct platform_profile_handler *cur_profile; static DEFINE_MUTEX(profile_lock); static const char * const profile_names[] = { @@ -19,99 +19,365 @@ static const char * const profile_names[] = { [PLATFORM_PROFILE_BALANCED] = "balanced", [PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance", [PLATFORM_PROFILE_PERFORMANCE] = "performance", + [PLATFORM_PROFILE_CUSTOM] = "custom", }; static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST); -static ssize_t platform_profile_choices_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static DEFINE_IDA(platform_profile_ida); + +/** + * _commmon_choices_show - Show the available profile choices + * @choices: The available profile choices + * @buf: The buffer to write to + * + * Return: The number of bytes written + */ +static ssize_t _commmon_choices_show(unsigned long *choices, char *buf) { - int len = 0; - int err, i; + int i, len = 0; - err = mutex_lock_interruptible(&profile_lock); - if (err) - return err; - - if (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; - } - - for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) { + for_each_set_bit(i, choices, PLATFORM_PROFILE_LAST) { if (len == 0) len += sysfs_emit_at(buf, len, "%s", profile_names[i]); else len += sysfs_emit_at(buf, len, " %s", profile_names[i]); } len += sysfs_emit_at(buf, len, "\n"); - mutex_unlock(&profile_lock); + return len; } -static ssize_t platform_profile_show(struct device *dev, - struct device_attribute *attr, - char *buf) +/** + * _store_class_profile - Set the profile for a class device + * @dev: The class device + * @data: The profile to set + * + * Return: 0 on success, -errno on failure + */ +static int _store_class_profile(struct device *dev, void *data) { - enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED; + struct platform_profile_handler *handler; + int *bit = (int *)data; + + lockdep_assert_held(&profile_lock); + handler = dev_get_drvdata(dev); + if (!test_bit(*bit, handler->choices)) + return -EOPNOTSUPP; + + return handler->profile_set(handler, *bit); +} + +/** + * _notify_class_profile - Notify the class device of a profile change + * @dev: The class device + * @data: Unused + * + * Return: 0 on success, -errno on failure + */ +static int _notify_class_profile(struct device *dev, void *data) +{ + struct platform_profile_handler *handler = dev_get_drvdata(dev); + + lockdep_assert_held(&profile_lock); + sysfs_notify(&handler->class_dev->kobj, NULL, "profile"); + kobject_uevent(&handler->class_dev->kobj, KOBJ_CHANGE); + + return 0; +} + +/** + * get_class_profile - Show the current profile for a class device + * @dev: The class device + * @profile: The profile to return + * + * Return: 0 on success, -errno on failure + */ +static int get_class_profile(struct device *dev, + enum platform_profile_option *profile) +{ + struct platform_profile_handler *handler; + enum platform_profile_option val; int err; - err = mutex_lock_interruptible(&profile_lock); - if (err) + lockdep_assert_held(&profile_lock); + handler = dev_get_drvdata(dev); + err = handler->profile_get(handler, &val); + if (err) { + pr_err("Failed to get profile for handler %s\n", handler->name); return err; - - if (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; } - err = cur_profile->profile_get(cur_profile, &profile); - mutex_unlock(&profile_lock); - if (err) - return err; + if (WARN_ON(val >= PLATFORM_PROFILE_LAST)) + return -EINVAL; + *profile = val; - /* Check that profile is valid index */ - if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names)))) - return -EIO; + return 0; +} + +/** + * name_show - Show the name of the profile handler + * @dev: The device + * @attr: The attribute + * @buf: The buffer to write to + * + * Return: The number of bytes written + */ +static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_profile_handler *handler = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", handler->name); +} +static DEVICE_ATTR_RO(name); + +/** + * choices_show - Show the available profile choices + * @dev: The device + * @attr: The attribute + * @buf: The buffer to write to + * + * Return: The number of bytes written + */ +static ssize_t choices_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_profile_handler *handler = dev_get_drvdata(dev); + + return _commmon_choices_show(handler->choices, buf); +} +static DEVICE_ATTR_RO(choices); + +/** + * profile_show - Show the current profile for a class device + * @dev: The device + * @attr: The attribute + * @buf: The buffer to write to + * + * Return: The number of bytes written + */ +static ssize_t profile_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + enum platform_profile_option profile = PLATFORM_PROFILE_LAST; + int err; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + err = get_class_profile(dev, &profile); + if (err) + return err; + } return sysfs_emit(buf, "%s\n", profile_names[profile]); } -static ssize_t platform_profile_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +/** + * profile_store - Set the profile for a class device + * @dev: The device + * @attr: The attribute + * @buf: The buffer to read from + * @count: The number of bytes to read + * + * Return: The number of bytes read + */ +static ssize_t profile_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - int err, i; + int index, ret; - err = mutex_lock_interruptible(&profile_lock); + index = sysfs_match_string(profile_names, buf); + if (index < 0) + return -EINVAL; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + ret = _store_class_profile(dev, &index); + if (ret) + return ret; + } + + sysfs_notify(acpi_kobj, NULL, "platform_profile"); + + return count; +} +static DEVICE_ATTR_RW(profile); + +static struct attribute *profile_attrs[] = { + &dev_attr_name.attr, + &dev_attr_choices.attr, + &dev_attr_profile.attr, + NULL +}; +ATTRIBUTE_GROUPS(profile); + +static const struct class platform_profile_class = { + .name = "platform-profile", + .dev_groups = profile_groups, +}; + +/** + * _aggregate_choices - Aggregate the available profile choices + * @dev: The device + * @data: The available profile choices + * + * Return: 0 on success, -errno on failure + */ +static int _aggregate_choices(struct device *dev, void *data) +{ + struct platform_profile_handler *handler; + unsigned long *aggregate = data; + + lockdep_assert_held(&profile_lock); + handler = dev_get_drvdata(dev); + if (test_bit(PLATFORM_PROFILE_LAST, aggregate)) + bitmap_copy(aggregate, handler->choices, PLATFORM_PROFILE_LAST); + else + bitmap_and(aggregate, handler->choices, aggregate, PLATFORM_PROFILE_LAST); + + return 0; +} + +/** + * platform_profile_choices_show - Show the available profile choices for legacy sysfs interface + * @dev: The device + * @attr: The attribute + * @buf: The buffer to write to + * + * Return: The number of bytes written + */ +static ssize_t platform_profile_choices_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; + int err; + + set_bit(PLATFORM_PROFILE_LAST, aggregate); + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + err = class_for_each_device(&platform_profile_class, NULL, + aggregate, _aggregate_choices); + if (err) + return err; + } + + /* no profile handler registered any more */ + if (bitmap_empty(aggregate, PLATFORM_PROFILE_LAST)) + return -EINVAL; + + return _commmon_choices_show(aggregate, buf); +} + +/** + * _aggregate_profiles - Aggregate the profiles for legacy sysfs interface + * @dev: The device + * @data: The profile to return + * + * Return: 0 on success, -errno on failure + */ +static int _aggregate_profiles(struct device *dev, void *data) +{ + enum platform_profile_option *profile = data; + enum platform_profile_option val; + int err; + + err = get_class_profile(dev, &val); if (err) return err; - if (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; + if (*profile != PLATFORM_PROFILE_LAST && *profile != val) + *profile = PLATFORM_PROFILE_CUSTOM; + else + *profile = val; + + return 0; +} + +/** + * _store_and_notify - Store and notify a class from legacy sysfs interface + * @dev: The device + * @data: The profile to return + * + * Return: 0 on success, -errno on failure + */ +static int _store_and_notify(struct device *dev, void *data) +{ + enum platform_profile_option *profile = data; + int err; + + err = _store_class_profile(dev, profile); + if (err) + return err; + return _notify_class_profile(dev, NULL); +} + +/** + * platform_profile_show - Show the current profile for legacy sysfs interface + * @dev: The device + * @attr: The attribute + * @buf: The buffer to write to + * + * Return: The number of bytes written + */ +static ssize_t platform_profile_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + enum platform_profile_option profile = PLATFORM_PROFILE_LAST; + int err; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + err = class_for_each_device(&platform_profile_class, NULL, + &profile, _aggregate_profiles); + if (err) + return err; } + /* no profile handler registered any more */ + if (profile == PLATFORM_PROFILE_LAST) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", profile_names[profile]); +} + +/** + * platform_profile_store - Set the profile for legacy sysfs interface + * @dev: The device + * @attr: The attribute + * @buf: The buffer to read from + * @count: The number of bytes to read + * + * Return: The number of bytes read + */ +static ssize_t platform_profile_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; + int ret; + int i; + /* Scan for a matching profile */ i = sysfs_match_string(profile_names, buf); - if (i < 0) { - mutex_unlock(&profile_lock); + if (i < 0 || i == PLATFORM_PROFILE_CUSTOM) return -EINVAL; + set_bit(PLATFORM_PROFILE_LAST, choices); + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + ret = class_for_each_device(&platform_profile_class, NULL, + choices, _aggregate_choices); + if (ret) + return ret; + if (!test_bit(i, choices)) + return -EOPNOTSUPP; + + ret = class_for_each_device(&platform_profile_class, NULL, &i, + _store_and_notify); + if (ret) + return ret; } - /* Check that platform supports this profile choice */ - if (!test_bit(i, cur_profile->choices)) { - mutex_unlock(&profile_lock); - return -EOPNOTSUPP; - } + sysfs_notify(acpi_kobj, NULL, "platform_profile"); - err = cur_profile->profile_set(cur_profile, i); - if (!err) - sysfs_notify(acpi_kobj, NULL, "platform_profile"); - - mutex_unlock(&profile_lock); - if (err) - return err; return count; } @@ -124,54 +390,72 @@ static struct attribute *platform_profile_attrs[] = { NULL }; +static int profile_class_registered(struct device *dev, const void *data) +{ + return 1; +} + +static umode_t profile_class_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + if (!class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered)) + return 0; + return attr->mode; +} + static const struct attribute_group platform_profile_group = { - .attrs = platform_profile_attrs + .attrs = platform_profile_attrs, + .is_visible = profile_class_is_visible, }; -void platform_profile_notify(void) +void platform_profile_notify(struct platform_profile_handler *pprof) { - if (!cur_profile) - return; + scoped_cond_guard(mutex_intr, return, &profile_lock) { + _notify_class_profile(pprof->class_dev, NULL); + } sysfs_notify(acpi_kobj, NULL, "platform_profile"); } EXPORT_SYMBOL_GPL(platform_profile_notify); int platform_profile_cycle(void) { - enum platform_profile_option profile; - enum platform_profile_option next; + enum platform_profile_option next = PLATFORM_PROFILE_LAST; + enum platform_profile_option profile = PLATFORM_PROFILE_LAST; + unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; int err; - err = mutex_lock_interruptible(&profile_lock); - if (err) - return err; + set_bit(PLATFORM_PROFILE_LAST, choices); + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + err = class_for_each_device(&platform_profile_class, NULL, + &profile, _aggregate_profiles); + if (err) + return err; - if (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; + if (profile == PLATFORM_PROFILE_CUSTOM || + profile == PLATFORM_PROFILE_LAST) + return -EINVAL; + + err = class_for_each_device(&platform_profile_class, NULL, + choices, _aggregate_choices); + if (err) + return err; + + /* never iterate into a custom if all drivers supported it */ + clear_bit(PLATFORM_PROFILE_CUSTOM, choices); + + next = find_next_bit_wrap(choices, + PLATFORM_PROFILE_LAST, + profile + 1); + + err = class_for_each_device(&platform_profile_class, NULL, &next, + _store_and_notify); + + if (err) + return err; } - err = cur_profile->profile_get(cur_profile, &profile); - if (err) { - mutex_unlock(&profile_lock); - return err; - } + sysfs_notify(acpi_kobj, NULL, "platform_profile"); - next = find_next_bit_wrap(cur_profile->choices, PLATFORM_PROFILE_LAST, - profile + 1); - - if (WARN_ON(next == PLATFORM_PROFILE_LAST)) { - mutex_unlock(&profile_lock); - return -EINVAL; - } - - err = cur_profile->profile_set(cur_profile, next); - mutex_unlock(&profile_lock); - - if (!err) - sysfs_notify(acpi_kobj, NULL, "platform_profile"); - - return err; + return 0; } EXPORT_SYMBOL_GPL(platform_profile_cycle); @@ -179,43 +463,85 @@ int platform_profile_register(struct platform_profile_handler *pprof) { int err; - mutex_lock(&profile_lock); - /* We can only have one active profile */ - if (cur_profile) { - mutex_unlock(&profile_lock); - return -EEXIST; - } - - /* Sanity check the profile handler field are set */ + /* Sanity check the profile handler */ if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) || - !pprof->profile_set || !pprof->profile_get) { - mutex_unlock(&profile_lock); + !pprof->profile_set || !pprof->profile_get) { + pr_err("platform_profile: handler is invalid\n"); return -EINVAL; } - err = sysfs_create_group(acpi_kobj, &platform_profile_group); - if (err) { - mutex_unlock(&profile_lock); - return err; + guard(mutex)(&profile_lock); + + /* create class interface for individual handler */ + pprof->minor = ida_alloc(&platform_profile_ida, GFP_KERNEL); + if (pprof->minor < 0) + return pprof->minor; + pprof->class_dev = device_create(&platform_profile_class, pprof->dev, + MKDEV(0, 0), pprof, "platform-profile-%d", + pprof->minor); + if (IS_ERR(pprof->class_dev)) { + err = PTR_ERR(pprof->class_dev); + goto cleanup_ida; } - cur_profile = pprof; - mutex_unlock(&profile_lock); + sysfs_notify(acpi_kobj, NULL, "platform_profile"); + + err = sysfs_update_group(acpi_kobj, &platform_profile_group); + if (err) + goto cleanup_cur; + return 0; + +cleanup_cur: + device_unregister(pprof->class_dev); + +cleanup_ida: + ida_free(&platform_profile_ida, pprof->minor); + + return err; } EXPORT_SYMBOL_GPL(platform_profile_register); -int platform_profile_remove(void) +int platform_profile_remove(struct platform_profile_handler *pprof) { - sysfs_remove_group(acpi_kobj, &platform_profile_group); + int id; + guard(mutex)(&profile_lock); + + id = pprof->minor; + device_unregister(pprof->class_dev); + ida_free(&platform_profile_ida, id); + + sysfs_notify(acpi_kobj, NULL, "platform_profile"); + + sysfs_update_group(acpi_kobj, &platform_profile_group); - mutex_lock(&profile_lock); - cur_profile = NULL; - mutex_unlock(&profile_lock); return 0; } EXPORT_SYMBOL_GPL(platform_profile_remove); +static int __init platform_profile_init(void) +{ + int err; + + err = class_register(&platform_profile_class); + if (err) + return err; + + err = sysfs_create_group(acpi_kobj, &platform_profile_group); + if (err) + class_unregister(&platform_profile_class); + + return err; +} + +static void __exit platform_profile_exit(void) +{ + sysfs_remove_group(acpi_kobj, &platform_profile_group); + class_unregister(&platform_profile_class); +} +module_init(platform_profile_init); +module_exit(platform_profile_exit); + MODULE_AUTHOR("Mark Pearson "); MODULE_DESCRIPTION("ACPI platform profile sysfs interface"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 75dab01d43a7..b72712743cd7 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1162,127 +1162,6 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap) } } -/* NOTE: Keep this list in sync with drivers/platform/x86/dell-smo8800.c */ -static const char *const acpi_smo8800_ids[] = { - "SMO8800", - "SMO8801", - "SMO8810", - "SMO8811", - "SMO8820", - "SMO8821", - "SMO8830", - "SMO8831", -}; - -static acpi_status check_acpi_smo88xx_device(acpi_handle obj_handle, - u32 nesting_level, - void *context, - void **return_value) -{ - struct acpi_device_info *info; - acpi_status status; - char *hid; - int i; - - status = acpi_get_object_info(obj_handle, &info); - if (ACPI_FAILURE(status)) - return AE_OK; - - if (!(info->valid & ACPI_VALID_HID)) - goto smo88xx_not_found; - - hid = info->hardware_id.string; - if (!hid) - goto smo88xx_not_found; - - i = match_string(acpi_smo8800_ids, ARRAY_SIZE(acpi_smo8800_ids), hid); - if (i < 0) - goto smo88xx_not_found; - - kfree(info); - - *return_value = NULL; - return AE_CTRL_TERMINATE; - -smo88xx_not_found: - kfree(info); - return AE_OK; -} - -static bool is_dell_system_with_lis3lv02d(void) -{ - void *err = ERR_PTR(-ENOENT); - - if (!dmi_match(DMI_SYS_VENDOR, "Dell Inc.")) - return false; - - /* - * Check that ACPI device SMO88xx is present and is functioning. - * Function acpi_get_devices() already filters all ACPI devices - * which are not present or are not functioning. - * ACPI device SMO88xx represents our ST microelectronics lis3lv02d - * accelerometer but unfortunately ACPI does not provide any other - * information (like I2C address). - */ - acpi_get_devices(NULL, check_acpi_smo88xx_device, NULL, &err); - - return !IS_ERR(err); -} - -/* - * Accelerometer's I2C address is not specified in DMI nor ACPI, - * so it is needed to define mapping table based on DMI product names. - */ -static const struct { - const char *dmi_product_name; - unsigned short i2c_addr; -} dell_lis3lv02d_devices[] = { - /* - * Dell platform team told us that these Latitude devices have - * ST microelectronics accelerometer at I2C address 0x29. - */ - { "Latitude E5250", 0x29 }, - { "Latitude E5450", 0x29 }, - { "Latitude E5550", 0x29 }, - { "Latitude E6440", 0x29 }, - { "Latitude E6440 ATG", 0x29 }, - { "Latitude E6540", 0x29 }, - /* - * Additional individual entries were added after verification. - */ - { "Latitude 5480", 0x29 }, - { "Precision 3540", 0x29 }, - { "Vostro V131", 0x1d }, - { "Vostro 5568", 0x29 }, - { "XPS 15 7590", 0x29 }, -}; - -static void register_dell_lis3lv02d_i2c_device(struct i801_priv *priv) -{ - struct i2c_board_info info; - const char *dmi_product_name; - int i; - - dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME); - for (i = 0; i < ARRAY_SIZE(dell_lis3lv02d_devices); ++i) { - if (strcmp(dmi_product_name, - dell_lis3lv02d_devices[i].dmi_product_name) == 0) - break; - } - - if (i == ARRAY_SIZE(dell_lis3lv02d_devices)) { - dev_warn(&priv->pci_dev->dev, - "Accelerometer lis3lv02d is present on SMBus but its" - " address is unknown, skipping registration\n"); - return; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = dell_lis3lv02d_devices[i].i2c_addr; - strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE); - i2c_new_client_device(&priv->adapter, &info); -} - /* Register optional targets */ static void i801_probe_optional_targets(struct i801_priv *priv) { @@ -1302,9 +1181,6 @@ static void i801_probe_optional_targets(struct i801_priv *priv) if (dmi_name_in_vendors("FUJITSU")) dmi_walk(dmi_check_onboard_devices, &priv->adapter); - if (is_dell_system_with_lis3lv02d()) - register_dell_lis3lv02d_i2c_device(priv); - /* Instantiate SPD EEPROMs unless the SMBus is multiplexed */ #ifdef CONFIG_I2C_I801_MUX if (!priv->mux_pdev) diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index c5b36837e694..a2011af48201 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -939,7 +939,7 @@ MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { @@ -971,9 +971,9 @@ static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, return p - buf; } -static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { +static const struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { .attr = { .name = "bootfifo", .mode = 0400 }, - .read = mlxbf_bootctl_bootfifo_read, + .read_new = mlxbf_bootctl_bootfifo_read, }; static bool mlxbf_bootctl_guid_match(const guid_t *guid, diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 9d18dfca6a67..9ff7b487dc48 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -1168,7 +1168,7 @@ static int mlxbf_pmc_program_l3_counter(unsigned int blk_num, u32 cnt_num, u32 e /* Method to handle crspace counter programming */ static int mlxbf_pmc_program_crspace_counter(unsigned int blk_num, u32 cnt_num, u32 evt) { - void *addr; + void __iomem *addr; u32 word; int ret; @@ -1192,7 +1192,7 @@ static int mlxbf_pmc_program_crspace_counter(unsigned int blk_num, u32 cnt_num, /* Method to clear crspace counter value */ static int mlxbf_pmc_clear_crspace_counter(unsigned int blk_num, u32 cnt_num) { - void *addr; + void __iomem *addr; addr = pmc->block[blk_num].mmio_base + MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) + @@ -1405,7 +1405,7 @@ static int mlxbf_pmc_read_l3_event(unsigned int blk_num, u32 cnt_num, u64 *resul static int mlxbf_pmc_read_crspace_event(unsigned int blk_num, u32 cnt_num, u64 *result) { u32 word, evt; - void *addr; + void __iomem *addr; int ret; addr = pmc->block[blk_num].mmio_base + diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c index 08db878f1d7d..6c87e982bfc8 100644 --- a/drivers/platform/surface/surface_platform_profile.c +++ b/drivers/platform/surface/surface_platform_profile.c @@ -210,7 +210,10 @@ static int surface_platform_profile_probe(struct ssam_device *sdev) return -ENOMEM; tpd->sdev = sdev; + ssam_device_set_drvdata(sdev, tpd); + tpd->handler.name = "Surface Platform Profile"; + tpd->handler.dev = &sdev->dev; tpd->handler.profile_get = ssam_platform_profile_get; tpd->handler.profile_set = ssam_platform_profile_set; @@ -226,7 +229,10 @@ static int surface_platform_profile_probe(struct ssam_device *sdev) static void surface_platform_profile_remove(struct ssam_device *sdev) { - platform_profile_remove(); + struct ssam_platform_profile_device *tpd; + + tpd = ssam_device_get_drvdata(sdev); + platform_profile_remove(&tpd->handler); } static const struct ssam_device_id ssam_platform_profile_match[] = { diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index d09baa3d3d90..b3043d78a7b3 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include MODULE_AUTHOR("Carlos Corbacho"); @@ -70,7 +71,10 @@ MODULE_LICENSE("GPL"); #define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54 -#define ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK GENMASK(20, 8) +#define ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK GENMASK_ULL(7, 0) +#define ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK GENMASK_ULL(15, 8) +#define ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK GENMASK_ULL(23, 8) +#define ACER_PREDATOR_V4_SUPPORTED_SENSORS_BIT_MASK GENMASK_ULL(39, 24) /* * Acer ACPI method GUIDs @@ -98,9 +102,17 @@ enum acer_wmi_event_ids { }; enum acer_wmi_predator_v4_sys_info_command { - ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x02, - ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED = 0x0201, - ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED = 0x0601, + ACER_WMID_CMD_GET_PREDATOR_V4_SUPPORTED_SENSORS = 0x0000, + ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING = 0x0001, + ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x0002, +}; + +enum acer_wmi_predator_v4_sensor_id { + ACER_WMID_SENSOR_CPU_TEMPERATURE = 0x01, + ACER_WMID_SENSOR_CPU_FAN_SPEED = 0x02, + ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2 = 0x03, + ACER_WMID_SENSOR_GPU_FAN_SPEED = 0x06, + ACER_WMID_SENSOR_GPU_TEMPERATURE = 0x0A, }; static const struct key_entry acer_wmi_keymap[] __initconst = { @@ -246,7 +258,7 @@ struct hotkey_function_type_aa { #define ACER_CAP_TURBO_LED BIT(8) #define ACER_CAP_TURBO_FAN BIT(9) #define ACER_CAP_PLATFORM_PROFILE BIT(10) -#define ACER_CAP_FAN_SPEED_READ BIT(11) +#define ACER_CAP_HWMON BIT(11) /* * Interface type flags @@ -271,6 +283,7 @@ static u16 commun_func_bitmap; static u8 commun_fn_key_number; static bool cycle_gaming_thermal_profile = true; static bool predator_v4; +static u64 supported_sensors; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -358,7 +371,7 @@ static void __init set_quirks(void) if (quirks->predator_v4) interface->capability |= ACER_CAP_PLATFORM_PROFILE | - ACER_CAP_FAN_SPEED_READ; + ACER_CAP_HWMON; } static int __init dmi_matched(const struct dmi_system_id *dmi) @@ -393,6 +406,13 @@ static struct quirk_entry quirk_acer_predator_ph315_53 = { .gpu_fans = 1, }; +static struct quirk_entry quirk_acer_predator_pt14_51 = { + .turbo = 1, + .cpu_fans = 1, + .gpu_fans = 1, + .predator_v4 = 1, +}; + static struct quirk_entry quirk_acer_predator_v4 = { .predator_v4 = 1, }; @@ -600,6 +620,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = { }, .driver_data = &quirk_acer_predator_v4, }, + { + .callback = dmi_matched, + .ident = "Acer Predator PT14-51", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Predator PT14-51"), + }, + .driver_data = &quirk_acer_predator_pt14_51, + }, { .callback = set_force_caps, .ident = "Acer Aspire Switch 10E SW3-016", @@ -1497,6 +1526,24 @@ static acpi_status WMID_gaming_get_u64(u64 *value, u32 cap) return status; } +static int WMID_gaming_get_sys_info(u32 command, u64 *out) +{ + acpi_status status; + u64 result; + + status = WMI_gaming_execute_u64(ACER_WMID_GET_GAMING_SYS_INFO_METHODID, command, &result); + if (ACPI_FAILURE(status)) + return -EIO; + + /* The return status must be zero for the operation to have succeeded */ + if (FIELD_GET(ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK, result)) + return -EIO; + + *out = result; + + return 0; +} + static void WMID_gaming_set_fan_mode(u8 fan_mode) { /* fan_mode = 1 is used for auto, fan_mode = 2 used for turbo*/ @@ -1744,26 +1791,6 @@ static int acer_gsensor_event(void) return 0; } -static int acer_get_fan_speed(int fan) -{ - if (quirks->predator_v4) { - acpi_status status; - u64 fanspeed; - - status = WMI_gaming_execute_u64( - ACER_WMID_GET_GAMING_SYS_INFO_METHODID, - fan == 0 ? ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED : - ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED, - &fanspeed); - - if (ACPI_FAILURE(status)) - return -EIO; - - return FIELD_GET(ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK, fanspeed); - } - return -EOPNOTSUPP; -} - /* * Predator series turbo button */ @@ -1873,11 +1900,13 @@ acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof, return 0; } -static int acer_platform_profile_setup(void) +static int acer_platform_profile_setup(struct platform_device *device) { if (quirks->predator_v4) { int err; + platform_profile_handler.name = "acer-wmi"; + platform_profile_handler.dev = &device->dev; platform_profile_handler.profile_get = acer_predator_v4_platform_profile_get; platform_profile_handler.profile_set = @@ -1926,12 +1955,9 @@ static int acer_thermal_profile_change(void) return err; /* Check power source */ - status = WMI_gaming_execute_u64( - ACER_WMID_GET_GAMING_SYS_INFO_METHODID, - ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS, &on_AC); - - if (ACPI_FAILURE(status)) - return -EIO; + err = WMID_gaming_get_sys_info(ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS, &on_AC); + if (err < 0) + return err; switch (current_tp) { case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO: @@ -1986,7 +2012,7 @@ static int acer_thermal_profile_change(void) if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI) last_non_turbo_profile = tp; - platform_profile_notify(); + platform_profile_notify(&platform_profile_handler); } return 0; @@ -2530,12 +2556,12 @@ static int acer_platform_probe(struct platform_device *device) goto error_rfkill; if (has_cap(ACER_CAP_PLATFORM_PROFILE)) { - err = acer_platform_profile_setup(); + err = acer_platform_profile_setup(device); if (err) goto error_platform_profile; } - if (has_cap(ACER_CAP_FAN_SPEED_READ)) { + if (has_cap(ACER_CAP_HWMON)) { err = acer_wmi_hwmon_init(); if (err) goto error_hwmon; @@ -2545,7 +2571,7 @@ static int acer_platform_probe(struct platform_device *device) error_hwmon: if (platform_profile_support) - platform_profile_remove(); + platform_profile_remove(&platform_profile_handler); error_platform_profile: acer_rfkill_exit(); error_rfkill: @@ -2568,7 +2594,7 @@ static void acer_platform_remove(struct platform_device *device) acer_rfkill_exit(); if (platform_profile_support) - platform_profile_remove(); + platform_profile_remove(&platform_profile_handler); } #ifdef CONFIG_PM_SLEEP @@ -2655,43 +2681,86 @@ static void __init create_debugfs(void) &interface->debug.wmid_devices); } +static const enum acer_wmi_predator_v4_sensor_id acer_wmi_temp_channel_to_sensor_id[] = { + [0] = ACER_WMID_SENSOR_CPU_TEMPERATURE, + [1] = ACER_WMID_SENSOR_GPU_TEMPERATURE, + [2] = ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2, +}; + +static const enum acer_wmi_predator_v4_sensor_id acer_wmi_fan_channel_to_sensor_id[] = { + [0] = ACER_WMID_SENSOR_CPU_FAN_SPEED, + [1] = ACER_WMID_SENSOR_GPU_FAN_SPEED, +}; + static umode_t acer_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { + enum acer_wmi_predator_v4_sensor_id sensor_id; + const u64 *supported_sensors = data; + switch (type) { + case hwmon_temp: + sensor_id = acer_wmi_temp_channel_to_sensor_id[channel]; + break; case hwmon_fan: - if (acer_get_fan_speed(channel) >= 0) - return 0444; + sensor_id = acer_wmi_fan_channel_to_sensor_id[channel]; break; default: return 0; } + if (*supported_sensors & BIT(sensor_id - 1)) + return 0444; + return 0; } static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { + u64 command = ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING; + u64 result; int ret; switch (type) { - case hwmon_fan: - ret = acer_get_fan_speed(channel); + case hwmon_temp: + command |= FIELD_PREP(ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK, + acer_wmi_temp_channel_to_sensor_id[channel]); + + ret = WMID_gaming_get_sys_info(command, &result); if (ret < 0) return ret; - *val = ret; - break; + + result = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result); + *val = result * MILLIDEGREE_PER_DEGREE; + return 0; + case hwmon_fan: + command |= FIELD_PREP(ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK, + acer_wmi_fan_channel_to_sensor_id[channel]); + + ret = WMID_gaming_get_sys_info(command, &result); + if (ret < 0) + return ret; + + *val = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result); + return 0; default: return -EOPNOTSUPP; } - - return 0; } static const struct hwmon_channel_info *const acer_wmi_hwmon_info[] = { - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), NULL + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, + HWMON_F_INPUT + ), + NULL }; static const struct hwmon_ops acer_wmi_hwmon_ops = { @@ -2708,9 +2777,20 @@ static int acer_wmi_hwmon_init(void) { struct device *dev = &acer_platform_device->dev; struct device *hwmon; + u64 result; + int ret; + + ret = WMID_gaming_get_sys_info(ACER_WMID_CMD_GET_PREDATOR_V4_SUPPORTED_SENSORS, &result); + if (ret < 0) + return ret; + + /* Return early if no sensors are available */ + supported_sensors = FIELD_GET(ACER_PREDATOR_V4_SUPPORTED_SENSORS_BIT_MASK, result); + if (!supported_sensors) + return 0; hwmon = devm_hwmon_device_register_with_info(dev, "acer", - &acer_platform_driver, + &supported_sensors, &acer_wmi_hwmon_chip_info, NULL); diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index e981d45e1c12..444b43be35a2 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -226,7 +226,7 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind) } static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); @@ -285,19 +285,19 @@ static int init_acpi(struct device *dev) return ret; } -static struct bin_attribute hsmp_metric_tbl_attr = { +static const struct bin_attribute hsmp_metric_tbl_attr = { .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, - .read = hsmp_metric_tbl_acpi_read, + .read_new = hsmp_metric_tbl_acpi_read, .size = sizeof(struct hsmp_metric_table), }; -static struct bin_attribute *hsmp_attr_list[] = { +static const struct bin_attribute *hsmp_attr_list[] = { &hsmp_metric_tbl_attr, NULL }; -static struct attribute_group hsmp_attr_grp = { - .bin_attrs = hsmp_attr_list, +static const struct attribute_group hsmp_attr_grp = { + .bin_attrs_new = hsmp_attr_list, .is_bin_visible = hsmp_is_sock_attr_visible, }; diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 227b4ad4a51a..03164e30b3a5 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -33,7 +33,13 @@ #define HSMP_WR true #define HSMP_RD false -#define DRIVER_VERSION "2.3" +#define DRIVER_VERSION "2.4" + +/* + * When same message numbers are used for both GET and SET operation, + * bit:31 indicates whether its SET or GET operation. + */ +#define CHECK_GET_BIT BIT(31) static struct hsmp_plat_device hsmp_pdev; @@ -167,11 +173,28 @@ static int validate_message(struct hsmp_message *msg) if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) return -ENOMSG; - /* num_args and response_sz against the HSMP spec */ - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args || - msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + /* + * num_args passed by user should match the num_args specified in + * message description table. + */ + if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) return -EINVAL; + /* + * Some older HSMP SET messages are updated to add GET in the same message. + * In these messages, GET returns the current value and SET also returns + * the successfully set value. To support this GET and SET in same message + * while maintaining backward compatibility for the HSMP users, + * hsmp_msg_desc_table[] indicates only maximum allowed response_sz. + */ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET) { + if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + } else { + /* only HSMP_SET or HSMP_GET messages go through this strict check */ + if (msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + } return 0; } @@ -239,6 +262,18 @@ int hsmp_test(u16 sock_ind, u32 value) } EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); +static bool is_get_msg(struct hsmp_message *msg) +{ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) + return true; + + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && + (msg->args[0] & CHECK_GET_BIT)) + return true; + + return false; +} + long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int __user *arguser = (int __user *)arg; @@ -261,7 +296,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) * Device is opened in O_WRONLY mode * Execute only set/configure commands */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET) + if (is_get_msg(&msg)) return -EPERM; break; case FMODE_READ: @@ -269,7 +304,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) * Device is opened in O_RDONLY mode * Execute only get/monitor commands */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET) + if (!is_get_msg(&msg)) return -EPERM; break; case FMODE_READ | FMODE_WRITE: diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index a61f815c9f80..02ca85762b68 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -59,7 +59,7 @@ static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, } static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct hsmp_socket *sock; @@ -97,13 +97,13 @@ static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, * is_bin_visible function is used to show / hide the necessary groups. */ #define HSMP_BIN_ATTR(index, _list) \ -static struct bin_attribute attr##index = { \ +static const struct bin_attribute attr##index = { \ .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, \ .private = (void *)index, \ - .read = hsmp_metric_tbl_plat_read, \ + .read_new = hsmp_metric_tbl_plat_read, \ .size = sizeof(struct hsmp_metric_table), \ }; \ -static struct bin_attribute _list[] = { \ +static const struct bin_attribute _list[] = { \ &attr##index, \ NULL \ } @@ -118,8 +118,8 @@ HSMP_BIN_ATTR(6, *sock6_attr_list); HSMP_BIN_ATTR(7, *sock7_attr_list); #define HSMP_BIN_ATTR_GRP(index, _list, _name) \ -static struct attribute_group sock##index##_attr_grp = { \ - .bin_attrs = _list, \ +static const struct attribute_group sock##index##_attr_grp = { \ + .bin_attrs_new = _list, \ .is_bin_visible = hsmp_is_sock_attr_visible, \ .name = #_name, \ } diff --git a/drivers/platform/x86/amd/pmc/Makefile b/drivers/platform/x86/amd/pmc/Makefile index f1d9ab19d24c..255d94ddf999 100644 --- a/drivers/platform/x86/amd/pmc/Makefile +++ b/drivers/platform/x86/amd/pmc/Makefile @@ -4,6 +4,6 @@ # AMD Power Management Controller Driver # -amd-pmc-objs := pmc.o pmc-quirks.o +amd-pmc-objs := pmc.o pmc-quirks.o mp1_stb.o obj-$(CONFIG_AMD_PMC) += amd-pmc.o amd-pmc-$(CONFIG_AMD_MP2_STB) += mp2_stb.o diff --git a/drivers/platform/x86/amd/pmc/mp1_stb.c b/drivers/platform/x86/amd/pmc/mp1_stb.c new file mode 100644 index 000000000000..c005f00988f7 --- /dev/null +++ b/drivers/platform/x86/amd/pmc/mp1_stb.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD MP1 Smart Trace Buffer (STB) Layer + * + * Copyright (c) 2024, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Authors: Shyam Sundar S K + * Sanket Goswami + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +#include "pmc.h" + +/* STB Spill to DRAM Parameters */ +#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 +#define S2D_TELEMETRY_BYTES_MAX 0x100000U +#define S2D_RSVD_RAM_SPACE 0x100000 + +/* STB Registers */ +#define AMD_STB_PMI_0 0x03E30600 +#define AMD_PMC_STB_DUMMY_PC 0xC6000007 + +/* STB Spill to DRAM Message Definition */ +#define STB_FORCE_FLUSH_DATA 0xCF +#define FIFO_SIZE 4096 + +/* STB S2D(Spill to DRAM) has different message port offset */ +#define AMD_S2D_REGISTER_MESSAGE 0xA20 +#define AMD_S2D_REGISTER_RESPONSE 0xA80 +#define AMD_S2D_REGISTER_ARGUMENT 0xA88 + +/* STB S2D (Spill to DRAM) message port offset for 44h model */ +#define AMD_GNR_REGISTER_MESSAGE 0x524 +#define AMD_GNR_REGISTER_RESPONSE 0x570 +#define AMD_GNR_REGISTER_ARGUMENT 0xA40 + +static bool enable_stb; +module_param(enable_stb, bool, 0644); +MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); + +static bool dump_custom_stb; +module_param(dump_custom_stb, bool, 0644); +MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); + +enum s2d_arg { + S2D_TELEMETRY_SIZE = 0x01, + S2D_PHYS_ADDR_LOW, + S2D_PHYS_ADDR_HIGH, + S2D_NUM_SAMPLES, + S2D_DRAM_SIZE, +}; + +struct amd_stb_v2_data { + size_t size; + u8 data[] __counted_by(size); +}; + +int amd_stb_write(struct amd_pmc_dev *dev, u32 data) +{ + int err; + + err = amd_smn_write(0, AMD_STB_PMI_0, data); + if (err) { + dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_STB_PMI_0); + return pcibios_err_to_errno(err); + } + + return 0; +} + +int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf) +{ + int i, err; + + for (i = 0; i < FIFO_SIZE; i++) { + err = amd_smn_read(0, AMD_STB_PMI_0, buf++); + if (err) { + dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_STB_PMI_0); + return pcibios_err_to_errno(err); + } + } + + return 0; +} + +static int amd_stb_debugfs_open(struct inode *inode, struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + u32 size = FIFO_SIZE * sizeof(u32); + u32 *buf; + int rc; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = amd_stb_read(dev, buf); + if (rc) { + kfree(buf); + return rc; + } + + filp->private_data = buf; + return rc; +} + +static ssize_t amd_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) +{ + if (!filp->private_data) + return -EINVAL; + + return simple_read_from_buffer(buf, size, pos, filp->private_data, + FIFO_SIZE * sizeof(u32)); +} + +static int amd_stb_debugfs_release(struct inode *inode, struct file *filp) +{ + kfree(filp->private_data); + return 0; +} + +static const struct file_operations amd_stb_debugfs_fops = { + .owner = THIS_MODULE, + .open = amd_stb_debugfs_open, + .read = amd_stb_debugfs_read, + .release = amd_stb_debugfs_release, +}; + +/* Enhanced STB Firmware Reporting Mechanism */ +static int amd_stb_handle_efr(struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + struct amd_stb_v2_data *stb_data_arr; + u32 fsize; + + fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; + stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); + if (!stb_data_arr) + return -ENOMEM; + + stb_data_arr->size = fsize; + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); + filp->private_data = stb_data_arr; + + return 0; +} + +static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp) +{ + struct amd_pmc_dev *dev = filp->f_inode->i_private; + u32 fsize, num_samples, val, stb_rdptr_offset = 0; + struct amd_stb_v2_data *stb_data_arr; + int ret; + + /* Write dummy postcode while reading the STB buffer */ + ret = amd_stb_write(dev, AMD_PMC_STB_DUMMY_PC); + if (ret) + dev_err(dev->dev, "error writing to STB: %d\n", ret); + + /* Spill to DRAM num_samples uses separate SMU message port */ + dev->msg_port = MSG_PORT_S2D; + + ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); + if (ret) + dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); + + /* + * We have a custom stb size and the PMFW is supposed to give + * the enhanced dram size. Note that we land here only for the + * platforms that support enhanced dram size reporting. + */ + if (dump_custom_stb) + return amd_stb_handle_efr(filp); + + /* Get the num_samples to calculate the last push location */ + ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->stb_arg.s2d_msg_id, true); + /* Clear msg_port for other SMU operation */ + dev->msg_port = MSG_PORT_PMC; + if (ret) { + dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); + return ret; + } + + fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); + stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); + if (!stb_data_arr) + return -ENOMEM; + + stb_data_arr->size = fsize; + + /* + * Start capturing data from the last push location. + * This is for general cases, where the stb limits + * are meant for standard usage. + */ + if (num_samples > S2D_TELEMETRY_BYTES_MAX) { + /* First read oldest data starting 1 behind last write till end of ringbuffer */ + stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; + fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; + + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); + /* Second copy the newer samples from offset 0 - last write */ + memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset); + } else { + memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); + } + + filp->private_data = stb_data_arr; + + return 0; +} + +static ssize_t amd_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, + loff_t *pos) +{ + struct amd_stb_v2_data *data = filp->private_data; + + return simple_read_from_buffer(buf, size, pos, data->data, data->size); +} + +static int amd_stb_debugfs_release_v2(struct inode *inode, struct file *filp) +{ + kfree(filp->private_data); + return 0; +} + +static const struct file_operations amd_stb_debugfs_fops_v2 = { + .owner = THIS_MODULE, + .open = amd_stb_debugfs_open_v2, + .read = amd_stb_debugfs_read_v2, + .release = amd_stb_debugfs_release_v2, +}; + +static void amd_stb_update_args(struct amd_pmc_dev *dev) +{ + if (cpu_feature_enabled(X86_FEATURE_ZEN5)) + switch (boot_cpu_data.x86_model) { + case 0x44: + dev->stb_arg.msg = AMD_GNR_REGISTER_MESSAGE; + dev->stb_arg.arg = AMD_GNR_REGISTER_ARGUMENT; + dev->stb_arg.resp = AMD_GNR_REGISTER_RESPONSE; + return; + default: + break; + } + + dev->stb_arg.msg = AMD_S2D_REGISTER_MESSAGE; + dev->stb_arg.arg = AMD_S2D_REGISTER_ARGUMENT; + dev->stb_arg.resp = AMD_S2D_REGISTER_RESPONSE; +} + +static bool amd_is_stb_supported(struct amd_pmc_dev *dev) +{ + switch (dev->cpu_id) { + case AMD_CPU_ID_YC: + case AMD_CPU_ID_CB: + if (boot_cpu_data.x86_model == 0x44) + dev->stb_arg.s2d_msg_id = 0x9B; + else + dev->stb_arg.s2d_msg_id = 0xBE; + break; + case AMD_CPU_ID_PS: + dev->stb_arg.s2d_msg_id = 0x85; + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: + if (boot_cpu_data.x86_model == 0x70) + dev->stb_arg.s2d_msg_id = 0xF1; + else + dev->stb_arg.s2d_msg_id = 0xDE; + break; + default: + return false; + } + + amd_stb_update_args(dev); + return true; +} + +int amd_stb_s2d_init(struct amd_pmc_dev *dev) +{ + u32 phys_addr_low, phys_addr_hi; + u64 stb_phys_addr; + u32 size = 0; + int ret; + + if (!enable_stb) + return 0; + + if (amd_is_stb_supported(dev)) { + debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, + &amd_stb_debugfs_fops_v2); + } else { + debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, + &amd_stb_debugfs_fops); + return 0; + } + + /* Spill to DRAM feature uses separate SMU message port */ + dev->msg_port = MSG_PORT_S2D; + + amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true); + if (size != S2D_TELEMETRY_BYTES_MAX) + return -EIO; + + /* Get DRAM size */ + ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true); + if (ret || !dev->dram_size) + dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; + + /* Get STB DRAM address */ + amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->stb_arg.s2d_msg_id, true); + amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->stb_arg.s2d_msg_id, true); + + stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); + + /* Clear msg_port for other SMU operation */ + dev->msg_port = MSG_PORT_PMC; + + dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size); + if (!dev->stb_virt_addr) + return -ENOMEM; + + return 0; +} diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 26b878ee5191..bfdf63ecfc80 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -40,24 +41,9 @@ #define AMD_PMC_SCRATCH_REG_1AH 0xF14 /* STB Registers */ -#define AMD_PMC_STB_PMI_0 0x03E30600 #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 #define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003 -#define AMD_PMC_STB_DUMMY_PC 0xC6000007 - -/* STB S2D(Spill to DRAM) has different message port offset */ -#define AMD_S2D_REGISTER_MESSAGE 0xA20 -#define AMD_S2D_REGISTER_RESPONSE 0xA80 -#define AMD_S2D_REGISTER_ARGUMENT 0xA88 - -/* STB Spill to DRAM Parameters */ -#define S2D_TELEMETRY_BYTES_MAX 0x100000U -#define S2D_RSVD_RAM_SPACE 0x100000 -#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 - -/* STB Spill to DRAM Message Definition */ -#define STB_FORCE_FLUSH_DATA 0xCF /* Base address of SMU for mapping physical address to virtual address */ #define AMD_PMC_MAPPING_SIZE 0x01000 @@ -97,7 +83,6 @@ #define DELAY_MIN_US 2000 #define DELAY_MAX_US 3000 -#define FIFO_SIZE 4096 enum amd_pmc_def { MSG_TEST = 0x01, @@ -105,24 +90,39 @@ enum amd_pmc_def { MSG_OS_HINT_RN, }; -enum s2d_arg { - S2D_TELEMETRY_SIZE = 0x01, - S2D_PHYS_ADDR_LOW, - S2D_PHYS_ADDR_HIGH, - S2D_NUM_SAMPLES, - S2D_DRAM_SIZE, -}; - -struct amd_pmc_stb_v2_data { - size_t size; - u8 data[] __counted_by(size); -}; - struct amd_pmc_bit_map { const char *name; u32 bit_mask; }; +static const struct amd_pmc_bit_map soc15_ip_blk_v2[] = { + {"DISPLAY", BIT(0)}, + {"CPU", BIT(1)}, + {"GFX", BIT(2)}, + {"VDD", BIT(3)}, + {"VDD_CCX", BIT(4)}, + {"ACP", BIT(5)}, + {"VCN_0", BIT(6)}, + {"VCN_1", BIT(7)}, + {"ISP", BIT(8)}, + {"NBIO", BIT(9)}, + {"DF", BIT(10)}, + {"USB3_0", BIT(11)}, + {"USB3_1", BIT(12)}, + {"LAPIC", BIT(13)}, + {"USB3_2", BIT(14)}, + {"USB4_RT0", BIT(15)}, + {"USB4_RT1", BIT(16)}, + {"USB4_0", BIT(17)}, + {"USB4_1", BIT(18)}, + {"MPM", BIT(19)}, + {"JPEG_0", BIT(20)}, + {"JPEG_1", BIT(21)}, + {"IPU", BIT(22)}, + {"UMSCH", BIT(23)}, + {"VPE", BIT(24)}, +}; + static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"DISPLAY", BIT(0)}, {"CPU", BIT(1)}, @@ -146,25 +146,13 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"IPU", BIT(19)}, {"UMSCH", BIT(20)}, {"VPE", BIT(21)}, - {} }; -static bool enable_stb; -module_param(enable_stb, bool, 0644); -MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); - static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); -static bool dump_custom_stb; -module_param(dump_custom_stb, bool, 0644); -MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer"); - static struct amd_pmc_dev pmc; -static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); -static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf); -static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data); static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) { @@ -193,155 +181,6 @@ struct smu_metrics { u64 timecondition_notmet_totaltime[32]; } __packed; -static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp) -{ - struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 size = FIFO_SIZE * sizeof(u32); - u32 *buf; - int rc; - - buf = kzalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - rc = amd_pmc_read_stb(dev, buf); - if (rc) { - kfree(buf); - return rc; - } - - filp->private_data = buf; - return rc; -} - -static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, - loff_t *pos) -{ - if (!filp->private_data) - return -EINVAL; - - return simple_read_from_buffer(buf, size, pos, filp->private_data, - FIFO_SIZE * sizeof(u32)); -} - -static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp) -{ - kfree(filp->private_data); - return 0; -} - -static const struct file_operations amd_pmc_stb_debugfs_fops = { - .owner = THIS_MODULE, - .open = amd_pmc_stb_debugfs_open, - .read = amd_pmc_stb_debugfs_read, - .release = amd_pmc_stb_debugfs_release, -}; - -/* Enhanced STB Firmware Reporting Mechanism */ -static int amd_pmc_stb_handle_efr(struct file *filp) -{ - struct amd_pmc_dev *dev = filp->f_inode->i_private; - struct amd_pmc_stb_v2_data *stb_data_arr; - u32 fsize; - - fsize = dev->dram_size - S2D_RSVD_RAM_SPACE; - stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); - if (!stb_data_arr) - return -ENOMEM; - - stb_data_arr->size = fsize; - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); - filp->private_data = stb_data_arr; - - return 0; -} - -static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) -{ - struct amd_pmc_dev *dev = filp->f_inode->i_private; - u32 fsize, num_samples, val, stb_rdptr_offset = 0; - struct amd_pmc_stb_v2_data *stb_data_arr; - int ret; - - /* Write dummy postcode while reading the STB buffer */ - ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC); - if (ret) - dev_err(dev->dev, "error writing to STB: %d\n", ret); - - /* Spill to DRAM num_samples uses separate SMU message port */ - dev->msg_port = 1; - - ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1); - if (ret) - dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret); - - /* - * We have a custom stb size and the PMFW is supposed to give - * the enhanced dram size. Note that we land here only for the - * platforms that support enhanced dram size reporting. - */ - if (dump_custom_stb) - return amd_pmc_stb_handle_efr(filp); - - /* Get the num_samples to calculate the last push location */ - ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true); - /* Clear msg_port for other SMU operation */ - dev->msg_port = 0; - if (ret) { - dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); - return ret; - } - - fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); - stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL); - if (!stb_data_arr) - return -ENOMEM; - - stb_data_arr->size = fsize; - - /* - * Start capturing data from the last push location. - * This is for general cases, where the stb limits - * are meant for standard usage. - */ - if (num_samples > S2D_TELEMETRY_BYTES_MAX) { - /* First read oldest data starting 1 behind last write till end of ringbuffer */ - stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX; - fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset; - - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize); - /* Second copy the newer samples from offset 0 - last write */ - memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset); - } else { - memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize); - } - - filp->private_data = stb_data_arr; - - return 0; -} - -static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, - loff_t *pos) -{ - struct amd_pmc_stb_v2_data *data = filp->private_data; - - return simple_read_from_buffer(buf, size, pos, data->data, data->size); -} - -static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp) -{ - kfree(filp->private_data); - return 0; -} - -static const struct file_operations amd_pmc_stb_debugfs_fops_v2 = { - .owner = THIS_MODULE, - .open = amd_pmc_stb_debugfs_open_v2, - .read = amd_pmc_stb_debugfs_read_v2, - .release = amd_pmc_stb_debugfs_release_v2, -}; - static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) { switch (dev->cpu_id) { @@ -350,18 +189,23 @@ static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) case AMD_CPU_ID_YC: case AMD_CPU_ID_CB: dev->num_ips = 12; - dev->s2d_msg_id = 0xBE; + dev->ips_ptr = soc15_ip_blk; dev->smu_msg = 0x538; break; case AMD_CPU_ID_PS: dev->num_ips = 21; - dev->s2d_msg_id = 0x85; + dev->ips_ptr = soc15_ip_blk; dev->smu_msg = 0x538; break; case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - dev->num_ips = 22; - dev->s2d_msg_id = 0xDE; + if (boot_cpu_data.x86_model == 0x70) { + dev->num_ips = ARRAY_SIZE(soc15_ip_blk_v2); + dev->ips_ptr = soc15_ip_blk_v2; + } else { + dev->num_ips = ARRAY_SIZE(soc15_ip_blk); + dev->ips_ptr = soc15_ip_blk; + } dev->smu_msg = 0x938; break; } @@ -529,8 +373,8 @@ static int smu_fw_info_show(struct seq_file *s, void *unused) seq_puts(s, "\n=== Active time (in us) ===\n"); for (idx = 0 ; idx < dev->num_ips ; idx++) { - if (soc15_ip_blk[idx].bit_mask & dev->active_ips) - seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name, + if (dev->ips_ptr[idx].bit_mask & dev->active_ips) + seq_printf(s, "%-8s : %lld\n", dev->ips_ptr[idx].name, table.timecondition_notmet_lastcapture[idx]); } @@ -625,20 +469,6 @@ static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) debugfs_remove_recursive(dev->dbgfs_dir); } -static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev) -{ - switch (dev->cpu_id) { - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - return true; - default: - return false; - } -} - static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) { dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL); @@ -648,14 +478,17 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) &s0ix_stats_fops); debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev, &amd_pmc_idlemask_fops); - /* Enable STB only when the module_param is set */ - if (enable_stb) { - if (amd_pmc_is_stb_supported(dev)) - debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, - &amd_pmc_stb_debugfs_fops_v2); - else - debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev, - &amd_pmc_stb_debugfs_fops); +} + +static char *amd_pmc_get_msg_port(struct amd_pmc_dev *dev) +{ + switch (dev->msg_port) { + case MSG_PORT_PMC: + return "PMC"; + case MSG_PORT_S2D: + return "S2D"; + default: + return "Invalid message port"; } } @@ -663,10 +496,10 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) { u32 value, message, argument, response; - if (dev->msg_port) { - message = AMD_S2D_REGISTER_MESSAGE; - argument = AMD_S2D_REGISTER_ARGUMENT; - response = AMD_S2D_REGISTER_RESPONSE; + if (dev->msg_port == MSG_PORT_S2D) { + message = dev->stb_arg.msg; + argument = dev->stb_arg.arg; + response = dev->stb_arg.resp; } else { message = dev->smu_msg; argument = AMD_PMC_REGISTER_ARGUMENT; @@ -674,26 +507,26 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) } value = amd_pmc_reg_read(dev, response); - dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", amd_pmc_get_msg_port(dev), value); value = amd_pmc_reg_read(dev, argument); - dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", amd_pmc_get_msg_port(dev), value); value = amd_pmc_reg_read(dev, message); - dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value); + dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", amd_pmc_get_msg_port(dev), value); } -static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) +int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) { int rc; u32 val, message, argument, response; mutex_lock(&dev->lock); - if (dev->msg_port) { - message = AMD_S2D_REGISTER_MESSAGE; - argument = AMD_S2D_REGISTER_ARGUMENT; - response = AMD_S2D_REGISTER_RESPONSE; + if (dev->msg_port == MSG_PORT_S2D) { + message = dev->stb_arg.msg; + argument = dev->stb_arg.arg; + response = dev->stb_arg.resp; } else { message = dev->smu_msg; argument = AMD_PMC_REGISTER_ARGUMENT; @@ -881,7 +714,7 @@ static void amd_pmc_s2idle_prepare(void) return; } - rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_PREPARE); + rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_PREPARE); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); } @@ -900,7 +733,7 @@ static void amd_pmc_s2idle_check(void) /* Dump the IdleMask before we add to the STB */ amd_pmc_idlemask_read(pdev, pdev->dev, NULL); - rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_CHECK); + rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); } @@ -927,7 +760,7 @@ static void amd_pmc_s2idle_restore(void) /* Let SMU know that we are looking for stats */ amd_pmc_dump_data(pdev); - rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE); + rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_RESTORE); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); @@ -975,74 +808,6 @@ static const struct pci_device_id pmc_pci_ids[] = { { } }; -static int amd_pmc_s2d_init(struct amd_pmc_dev *dev) -{ - u32 phys_addr_low, phys_addr_hi; - u64 stb_phys_addr; - u32 size = 0; - int ret; - - /* Spill to DRAM feature uses separate SMU message port */ - dev->msg_port = 1; - - amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true); - if (size != S2D_TELEMETRY_BYTES_MAX) - return -EIO; - - /* Get DRAM size */ - ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); - if (ret || !dev->dram_size) - dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; - - /* Get STB DRAM address */ - amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true); - amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true); - - if (!phys_addr_hi && !phys_addr_low) { - dev_err(dev->dev, "STB is not enabled on the system; disable enable_stb or contact system vendor\n"); - return -EINVAL; - } - - stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); - - /* Clear msg_port for other SMU operation */ - dev->msg_port = 0; - - dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size); - if (!dev->stb_virt_addr) - return -ENOMEM; - - return 0; -} - -static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data) -{ - int err; - - err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data); - if (err) { - dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0); - return pcibios_err_to_errno(err); - } - - return 0; -} - -static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf) -{ - int i, err; - - for (i = 0; i < FIFO_SIZE; i++) { - err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++); - if (err) { - dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0); - return pcibios_err_to_errno(err); - } - } - - return 0; -} - static int amd_pmc_probe(struct platform_device *pdev) { struct amd_pmc_dev *dev = &pmc; @@ -1100,12 +865,6 @@ static int amd_pmc_probe(struct platform_device *pdev) /* Get num of IP blocks within the SoC */ amd_pmc_get_ip_info(dev); - if (enable_stb && amd_pmc_is_stb_supported(dev)) { - err = amd_pmc_s2d_init(dev); - if (err) - goto err_pci_dev_put; - } - platform_set_drvdata(pdev, dev); if (IS_ENABLED(CONFIG_SUSPEND)) { err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); @@ -1116,6 +875,10 @@ static int amd_pmc_probe(struct platform_device *pdev) } amd_pmc_dbgfs_register(dev); + err = amd_stb_s2d_init(dev); + if (err) + goto err_pci_dev_put; + if (IS_ENABLED(CONFIG_AMD_MP2_STB)) amd_mp2_stb_init(dev); pm_report_max_hw_sleep(U64_MAX); diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index f1166d15c856..f43f0253b0f5 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -14,6 +14,11 @@ #include #include +enum s2d_msg_port { + MSG_PORT_PMC, + MSG_PORT_S2D, +}; + struct amd_mp2_dev { void __iomem *mmio; void __iomem *vslbase; @@ -25,24 +30,31 @@ struct amd_mp2_dev { bool is_stb_data; }; +struct stb_arg { + u32 s2d_msg_id; + u32 msg; + u32 arg; + u32 resp; +}; + struct amd_pmc_dev { void __iomem *regbase; void __iomem *smu_virt_addr; void __iomem *stb_virt_addr; void __iomem *fch_virt_addr; - bool msg_port; u32 base_addr; u32 cpu_id; - u32 active_ips; u32 dram_size; + u32 active_ips; + const struct amd_pmc_bit_map *ips_ptr; u32 num_ips; - u32 s2d_msg_id; u32 smu_msg; /* SMU version information */ u8 smu_program; u8 major; u8 minor; u8 rev; + u8 msg_port; struct device *dev; struct pci_dev *rdev; struct mutex lock; /* generic mutex lock */ @@ -50,6 +62,7 @@ struct amd_pmc_dev { struct quirk_entry *quirks; bool disable_8042_wakeup; struct amd_mp2_dev *mp2; + struct stb_arg stb_arg; }; void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); @@ -70,4 +83,9 @@ void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); #define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 #define PCI_DEVICE_ID_AMD_MP2_STB 0x172c +int amd_stb_s2d_init(struct amd_pmc_dev *dev); +int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf); +int amd_stb_write(struct amd_pmc_dev *dev, u32 data); +int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); + #endif /* PMC_H */ diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index 7d6079b02589..6b26e48ce8ad 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_AMD_PMF) += amd-pmf.o amd-pmf-objs := core.o acpi.o sps.o \ auto-mode.o cnqf.o \ - tee-if.o spc.o pmf-quirks.o + tee-if.o spc.o diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 1b9c7acf0ddf..21ccd677ffa4 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -321,6 +321,18 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req req, sizeof(*req)); } +static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data) +{ + struct amd_pmf_dev *pmf_dev = data; + int ret; + + guard(mutex)(&pmf_dev->cb_mutex); + + ret = apmf_get_sbios_requests_v2(pmf_dev, &pmf_dev->req); + if (ret) + dev_err(pmf_dev->dev, "Failed to get v2 SBIOS requests: %d\n", ret); +} + static void apmf_event_handler(acpi_handle handle, u32 event, void *data) { struct amd_pmf_dev *pmf_dev = data; @@ -430,6 +442,15 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev) apmf_event_handler(ahandle, 0, pmf_dev); } + if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) { + status = acpi_install_notify_handler(ahandle, ACPI_ALL_NOTIFY, + apmf_event_handler_v2, pmf_dev); + if (ACPI_FAILURE(status)) { + dev_err(pmf_dev->dev, "failed to install notify handler for custom BIOS inputs\n"); + return -ENODEV; + } + } + return 0; } @@ -480,6 +501,9 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) && is_apmf_func_supported(pmf_dev, APMF_FUNC_SBIOS_REQUESTS)) acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler); + + if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) + acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler_v2); } int apmf_acpi_init(struct amd_pmf_dev *pmf_dev) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 06a97c533cb8..01eb9ee1eccd 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -456,7 +456,6 @@ static int amd_pmf_probe(struct platform_device *pdev) mutex_init(&dev->lock); mutex_init(&dev->update_mutex); - amd_pmf_quirks_init(dev); apmf_acpi_init(dev); platform_set_drvdata(pdev, dev); amd_pmf_dbgfs_register(dev); diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c deleted file mode 100644 index 7cde5733b9ca..000000000000 --- a/drivers/platform/x86/amd/pmf/pmf-quirks.c +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * AMD Platform Management Framework Driver Quirks - * - * Copyright (c) 2024, Advanced Micro Devices, Inc. - * All Rights Reserved. - * - * Author: Mario Limonciello - */ - -#include - -#include "pmf.h" - -struct quirk_entry { - u32 supported_func; -}; - -static struct quirk_entry quirk_no_sps_bug = { - .supported_func = 0x4003, -}; - -static const struct dmi_system_id fwbug_list[] = { - { - .ident = "ROG Zephyrus G14", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "GA403U"), - }, - .driver_data = &quirk_no_sps_bug, - }, - { - .ident = "ROG Ally X", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "RC72LA"), - }, - .driver_data = &quirk_no_sps_bug, - }, - { - .ident = "ASUS TUF Gaming A14", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "FA401W"), - }, - .driver_data = &quirk_no_sps_bug, - }, - {} -}; - -void amd_pmf_quirks_init(struct amd_pmf_dev *dev) -{ - const struct dmi_system_id *dmi_id; - struct quirk_entry *quirks; - - dmi_id = dmi_first_match(fwbug_list); - if (!dmi_id) - return; - - quirks = dmi_id->driver_data; - if (quirks->supported_func) { - dev->supported_func = quirks->supported_func; - pr_info("Using supported funcs quirk to avoid %s platform firmware bug\n", - dmi_id->ident); - } -} diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index a79808fda1d8..56024eb40266 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -370,6 +370,8 @@ struct amd_pmf_dev { struct input_dev *pmf_idev; size_t mtable_size; struct resource *res; + struct apmf_sbios_req_v2 req; /* To get custom bios pending request */ + struct mutex cb_mutex; }; struct apmf_sps_prop_granular_v2 { @@ -616,6 +618,16 @@ enum ta_slider { TA_MAX, }; +enum apmf_smartpc_custom_bios_inputs { + APMF_SMARTPC_CUSTOM_BIOS_INPUT1, + APMF_SMARTPC_CUSTOM_BIOS_INPUT2, +}; + +enum apmf_preq_smartpc { + NOTIFY_CUSTOM_BIOS_INPUT1 = 5, + NOTIFY_CUSTOM_BIOS_INPUT2, +}; + /* Command ids for TA communication */ enum ta_pmf_command { TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, @@ -657,7 +669,8 @@ struct ta_pmf_condition_info { u32 power_slider; u32 lid_state; bool user_present; - u32 rsvd1[2]; + u32 bios_input1; + u32 bios_input2; u32 monitor_count; u32 rsvd2[2]; u32 bat_design; @@ -797,7 +810,4 @@ int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); -/* Quirk infrastructure */ -void amd_pmf_quirks_init(struct amd_pmf_dev *dev); - #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index 06226eb0eab3..05bed8206365 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -47,12 +47,35 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table * dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open"); dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away"); dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light); + dev_dbg(dev->dev, "Custom BIOS input1: %u\n", in->ev_info.bios_input1); + dev_dbg(dev->dev, "Custom BIOS input2: %u\n", in->ev_info.bios_input2); dev_dbg(dev->dev, "==== TA inputs END ====\n"); } #else void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {} #endif +static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev, + struct ta_pmf_enact_table *in) +{ + if (!pdev->req.pending_req) + return; + + switch (pdev->req.pending_req) { + case BIT(NOTIFY_CUSTOM_BIOS_INPUT1): + in->ev_info.bios_input1 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT1]; + break; + case BIT(NOTIFY_CUSTOM_BIOS_INPUT2): + in->ev_info.bios_input2 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT2]; + break; + default: + dev_dbg(pdev->dev, "Invalid preq for BIOS input: 0x%x\n", pdev->req.pending_req); + } + + /* Clear pending requests after handling */ + memset(&pdev->req, 0, sizeof(pdev->req)); +} + static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in) { u16 max, avg = 0; @@ -201,4 +224,5 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab amd_pmf_get_battery_info(dev, in); amd_pmf_get_slider_info(dev, in); amd_pmf_get_sensor_info(dev, in); + amd_pmf_get_custom_bios_inputs(dev, in); } diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 92f7fb22277d..bd2bd6cfc39a 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -405,6 +405,8 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) amd_pmf_set_sps_power_limits(dev); } + dev->pprof.name = "amd-pmf"; + dev->pprof.dev = dev->dev; dev->pprof.profile_get = amd_pmf_profile_get; dev->pprof.profile_set = amd_pmf_profile_set; @@ -424,5 +426,5 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) void amd_pmf_deinit_sps(struct amd_pmf_dev *dev) { - platform_profile_remove(); + platform_profile_remove(&dev->pprof); } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 8bd187e8b47f..b317a626281e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -3782,7 +3782,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, * Ensure that platform_profile updates userspace with the change to ensure * that platform_profile and throttle_thermal_policy_mode are in sync. */ - platform_profile_notify(); + platform_profile_notify(&asus->platform_profile_handler); return count; } @@ -3869,6 +3869,8 @@ static int platform_profile_setup(struct asus_wmi *asus) dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n"); + asus->platform_profile_handler.name = "asus-wmi"; + asus->platform_profile_handler.dev = dev; asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get; asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set; @@ -4843,7 +4845,7 @@ static int asus_wmi_add(struct platform_device *pdev) fail_custom_fan_curve: fail_platform_profile_setup: if (asus->platform_profile_support) - platform_profile_remove(); + platform_profile_remove(&asus->platform_profile_handler); fail_fan_boost_mode: fail_platform: kfree(asus); @@ -4870,7 +4872,7 @@ static void asus_wmi_remove(struct platform_device *device) asus_wmi_battery_exit(asus); if (asus->platform_profile_support) - platform_profile_remove(); + platform_profile_remove(&asus->platform_profile_handler); kfree(asus); } diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 2dddafb3f7fa..d09060aedd3f 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -152,6 +152,7 @@ config DELL_SMBIOS_SMM config DELL_SMO8800 tristate "Dell Latitude freefall driver (ACPI SMO88XX)" default m + depends on I2C depends on ACPI || COMPILE_TEST help Say Y here if you want to support SMO88XX freefall devices diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index 79d60f1bf4c1..bb3cbd470a46 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -15,6 +15,7 @@ dell-smbios-objs := dell-smbios-base.o dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o +obj-$(CONFIG_DELL_SMO8800) += dell-lis3lv02d.o obj-$(CONFIG_DELL_UART_BACKLIGHT) += dell-uart-backlight.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o dell-wmi-objs := dell-wmi-base.o diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c index 341d01d3e3e4..1293640daef9 100644 --- a/drivers/platform/x86/dell/alienware-wmi.c +++ b/drivers/platform/x86/dell/alienware-wmi.c @@ -1120,7 +1120,7 @@ static int thermal_profile_set(struct platform_profile_handler *pprof, return wmax_thermal_control(supported_thermal_profiles[profile]); } -static int create_thermal_profile(void) +static int create_thermal_profile(struct platform_device *platform_device) { u32 out_data; u8 sys_desc[4]; @@ -1168,6 +1168,8 @@ static int create_thermal_profile(void) pp_handler.profile_get = thermal_profile_get; pp_handler.profile_set = thermal_profile_set; + pp_handler.name = "alienware-wmi"; + pp_handler.dev = &platform_device->dev; return platform_profile_register(&pp_handler); } @@ -1175,7 +1177,7 @@ static int create_thermal_profile(void) static void remove_thermal_profile(void) { if (quirks->thermal) - platform_profile_remove(); + platform_profile_remove(&pp_handler); } static int __init alienware_wmi_init(void) @@ -1236,7 +1238,7 @@ static int __init alienware_wmi_init(void) } if (quirks->thermal) { - ret = create_thermal_profile(); + ret = create_thermal_profile(platform_device); if (ret) goto fail_prep_thermal_profile; } diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c index 0aeb8149c16b..8149be25fa26 100644 --- a/drivers/platform/x86/dell/dcdbas.c +++ b/drivers/platform/x86/dell/dcdbas.c @@ -163,7 +163,7 @@ static ssize_t smi_data_buf_size_store(struct device *dev, } static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { ssize_t ret; @@ -176,7 +176,7 @@ static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, } static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { ssize_t ret; @@ -636,9 +636,9 @@ static struct notifier_block dcdbas_reboot_nb = { .priority = INT_MIN }; -static DCDBAS_BIN_ATTR_RW(smi_data); +static const BIN_ATTR_ADMIN_RW(smi_data, 0); -static struct bin_attribute *dcdbas_bin_attrs[] = { +static const struct bin_attribute *const dcdbas_bin_attrs[] = { &bin_attr_smi_data, NULL }; @@ -662,7 +662,7 @@ static struct attribute *dcdbas_dev_attrs[] = { static const struct attribute_group dcdbas_attr_group = { .attrs = dcdbas_dev_attrs, - .bin_attrs = dcdbas_bin_attrs, + .bin_attrs_new = dcdbas_bin_attrs, }; static int dcdbas_probe(struct platform_device *dev) diff --git a/drivers/platform/x86/dell/dcdbas.h b/drivers/platform/x86/dell/dcdbas.h index 942a23ddded0..a05d7f667586 100644 --- a/drivers/platform/x86/dell/dcdbas.h +++ b/drivers/platform/x86/dell/dcdbas.h @@ -56,14 +56,6 @@ #define DCDBAS_DEV_ATTR_WO(_name) \ DEVICE_ATTR(_name,0200,NULL,_name##_store); -#define DCDBAS_BIN_ATTR_RW(_name) \ -struct bin_attribute bin_attr_##_name = { \ - .attr = { .name = __stringify(_name), \ - .mode = 0600 }, \ - .read = _name##_read, \ - .write = _name##_write, \ -} - struct smi_cmd { __u32 magic; __u32 ebx; diff --git a/drivers/platform/x86/dell/dell-lis3lv02d.c b/drivers/platform/x86/dell/dell-lis3lv02d.c new file mode 100644 index 000000000000..d2b34e10c5eb --- /dev/null +++ b/drivers/platform/x86/dell/dell-lis3lv02d.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lis3lv02d i2c-client instantiation for ACPI SMO88xx devices without I2C resources. + * + * Copyright (C) 2024 Hans de Goede + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include "dell-smo8800-ids.h" + +#define DELL_LIS3LV02D_DMI_ENTRY(product_name, i2c_addr) \ + { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), \ + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, product_name), \ + }, \ + .driver_data = (void *)(uintptr_t)(i2c_addr), \ + } + +/* + * Accelerometer's I2C address is not specified in DMI nor ACPI, + * so it is needed to define mapping table based on DMI product names. + */ +static const struct dmi_system_id lis3lv02d_devices[] __initconst = { + /* + * Dell platform team told us that these Latitude devices have + * ST microelectronics accelerometer at I2C address 0x29. + */ + DELL_LIS3LV02D_DMI_ENTRY("Latitude E5250", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E5450", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E5550", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6440", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6440 ATG", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6540", 0x29), + /* + * Additional individual entries were added after verification. + */ + DELL_LIS3LV02D_DMI_ENTRY("Latitude 5480", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6330", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Latitude E6430", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Precision 3540", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("Vostro V131", 0x1d), + DELL_LIS3LV02D_DMI_ENTRY("Vostro 5568", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("XPS 15 7590", 0x29), + DELL_LIS3LV02D_DMI_ENTRY("XPS 15 9550", 0x29), + { } +}; + +static u8 i2c_addr; +static struct i2c_client *i2c_dev; +static bool notifier_registered; + +static bool i2c_adapter_is_main_i801(struct i2c_adapter *adap) +{ + /* + * Only match the main I801 adapter and reject secondary adapters + * which names start with "SMBus I801 IDF adapter". + */ + return strstarts(adap->name, "SMBus I801 adapter"); +} + +static int find_i801(struct device *dev, void *data) +{ + struct i2c_adapter *adap, **adap_ret = data; + + adap = i2c_verify_adapter(dev); + if (!adap) + return 0; + + if (!i2c_adapter_is_main_i801(adap)) + return 0; + + *adap_ret = i2c_get_adapter(adap->nr); + return 1; +} + +static void instantiate_i2c_client(struct work_struct *work) +{ + struct i2c_board_info info = { }; + struct i2c_adapter *adap = NULL; + + if (i2c_dev) + return; + + /* + * bus_for_each_dev() and not i2c_for_each_dev() to avoid + * a deadlock when find_i801() calls i2c_get_adapter(). + */ + bus_for_each_dev(&i2c_bus_type, NULL, &adap, find_i801); + if (!adap) + return; + + info.addr = i2c_addr; + strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE); + + i2c_dev = i2c_new_client_device(adap, &info); + if (IS_ERR(i2c_dev)) { + dev_err(&adap->dev, "error %ld registering i2c_client\n", PTR_ERR(i2c_dev)); + i2c_dev = NULL; + } else { + dev_dbg(&adap->dev, "registered lis3lv02d on address 0x%02x\n", info.addr); + } + + i2c_put_adapter(adap); +} +static DECLARE_WORK(i2c_work, instantiate_i2c_client); + +static int i2c_bus_notify(struct notifier_block *nb, unsigned long action, void *data) +{ + struct device *dev = data; + struct i2c_client *client; + struct i2c_adapter *adap; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + adap = i2c_verify_adapter(dev); + if (!adap) + break; + + if (i2c_adapter_is_main_i801(adap)) + queue_work(system_long_wq, &i2c_work); + break; + case BUS_NOTIFY_REMOVED_DEVICE: + client = i2c_verify_client(dev); + if (!client) + break; + + if (i2c_dev == client) { + dev_dbg(&client->adapter->dev, "lis3lv02d i2c_client removed\n"); + i2c_dev = NULL; + } + break; + default: + break; + } + + return 0; +} +static struct notifier_block i2c_nb = { .notifier_call = i2c_bus_notify }; + +static int __init match_acpi_device_ids(struct device *dev, const void *data) +{ + return acpi_match_device(data, dev) ? 1 : 0; +} + +static int __init dell_lis3lv02d_init(void) +{ + const struct dmi_system_id *lis3lv02d_dmi_id; + struct device *dev; + int err; + + /* + * First check for a matching platform_device. This protects against + * SMO88xx ACPI fwnodes which actually do have an I2C resource, which + * will already have an i2c_client instantiated (not a platform_device). + */ + dev = bus_find_device(&platform_bus_type, NULL, smo8800_ids, match_acpi_device_ids); + if (!dev) { + pr_debug("No SMO88xx platform-device found\n"); + return 0; + } + put_device(dev); + + lis3lv02d_dmi_id = dmi_first_match(lis3lv02d_devices); + if (!lis3lv02d_dmi_id) { + pr_warn("accelerometer is present on SMBus but its address is unknown, skipping registration\n"); + return 0; + } + + i2c_addr = (long)lis3lv02d_dmi_id->driver_data; + + /* + * Register i2c-bus notifier + queue initial scan for lis3lv02d + * i2c_client instantiation. + */ + err = bus_register_notifier(&i2c_bus_type, &i2c_nb); + if (err) + return err; + + notifier_registered = true; + + queue_work(system_long_wq, &i2c_work); + return 0; +} +module_init(dell_lis3lv02d_init); + +static void __exit dell_lis3lv02d_module_exit(void) +{ + if (!notifier_registered) + return; + + bus_unregister_notifier(&i2c_bus_type, &i2c_nb); + cancel_work_sync(&i2c_work); + i2c_unregister_device(i2c_dev); +} +module_exit(dell_lis3lv02d_module_exit); + +MODULE_DESCRIPTION("lis3lv02d i2c-client instantiation for ACPI SMO88xx devices"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell/dell-pc.c b/drivers/platform/x86/dell/dell-pc.c index 972385ca1990..3797a5721dbd 100644 --- a/drivers/platform/x86/dell/dell-pc.c +++ b/drivers/platform/x86/dell/dell-pc.c @@ -18,10 +18,13 @@ #include #include #include +#include #include #include "dell-smbios.h" +static struct platform_device *platform_device; + static const struct dmi_system_id dell_device_table[] __initconst = { { .ident = "Dell Inc.", @@ -244,9 +247,17 @@ static int thermal_init(void) if (!supported_modes) return 0; - thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL); - if (!thermal_handler) - return -ENOMEM; + platform_device = platform_device_register_simple("dell-pc", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(platform_device)) + return PTR_ERR(platform_device); + + thermal_handler = devm_kzalloc(&platform_device->dev, sizeof(*thermal_handler), GFP_KERNEL); + if (!thermal_handler) { + ret = -ENOMEM; + goto cleanup_platform_device; + } + thermal_handler->name = "dell-pc"; + thermal_handler->dev = &platform_device->dev; thermal_handler->profile_get = thermal_platform_profile_get; thermal_handler->profile_set = thermal_platform_profile_set; @@ -261,20 +272,25 @@ static int thermal_init(void) /* Clean up if failed */ ret = platform_profile_register(thermal_handler); - if (ret) { - kfree(thermal_handler); - thermal_handler = NULL; - } + if (ret) + goto cleanup_thermal_handler; + + return 0; + +cleanup_thermal_handler: + thermal_handler = NULL; + +cleanup_platform_device: + platform_device_unregister(platform_device); return ret; } static void thermal_cleanup(void) { - if (thermal_handler) { - platform_profile_remove(); - kfree(thermal_handler); - } + if (thermal_handler) + platform_profile_remove(thermal_handler); + platform_device_unregister(platform_device); } static int __init dell_init(void) diff --git a/drivers/platform/x86/dell/dell-smo8800-ids.h b/drivers/platform/x86/dell/dell-smo8800-ids.h new file mode 100644 index 000000000000..ec58e229ba7a --- /dev/null +++ b/drivers/platform/x86/dell/dell-smo8800-ids.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ACPI SMO88XX lis3lv02d freefall / accelerometer device-ids. + * + * Copyright (C) 2012 Sonal Santan + * Copyright (C) 2014 Pali Rohár + */ +#ifndef _DELL_SMO8800_IDS_H_ +#define _DELL_SMO8800_IDS_H_ + +#include +#include + +static const struct acpi_device_id smo8800_ids[] = { + { "SMO8800" }, + { "SMO8801" }, + { "SMO8810" }, + { "SMO8811" }, + { "SMO8820" }, + { "SMO8821" }, + { "SMO8830" }, + { "SMO8831" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, smo8800_ids); + +#endif diff --git a/drivers/platform/x86/dell/dell-smo8800.c b/drivers/platform/x86/dell/dell-smo8800.c index 87fe03f23f24..8872f9b57fce 100644 --- a/drivers/platform/x86/dell/dell-smo8800.c +++ b/drivers/platform/x86/dell/dell-smo8800.c @@ -14,10 +14,10 @@ #include #include #include -#include #include #include #include +#include "dell-smo8800-ids.h" struct smo8800_device { u32 irq; /* acpi device irq */ @@ -163,20 +163,6 @@ static void smo8800_remove(struct platform_device *device) dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); } -/* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */ -static const struct acpi_device_id smo8800_ids[] = { - { "SMO8800", 0 }, - { "SMO8801", 0 }, - { "SMO8810", 0 }, - { "SMO8811", 0 }, - { "SMO8820", 0 }, - { "SMO8821", 0 }, - { "SMO8830", 0 }, - { "SMO8831", 0 }, - { "", 0 }, -}; -MODULE_DEVICE_TABLE(acpi, smo8800_ids); - static struct platform_driver smo8800_driver = { .probe = smo8800_probe, .remove = smo8800_remove, diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c index 6e5dc7e3674f..b1003c170ca1 100644 --- a/drivers/platform/x86/dell/dell-uart-backlight.c +++ b/drivers/platform/x86/dell/dell-uart-backlight.c @@ -159,7 +159,7 @@ static int dell_uart_set_bl_power(struct dell_uart_backlight *dell_bl, int power set_power[0] = DELL_SOF(SET_CMD_LEN); set_power[1] = CMD_SET_BL_POWER; - set_power[2] = (power == FB_BLANK_UNBLANK) ? 1 : 0; + set_power[2] = (power == BACKLIGHT_POWER_ON) ? 1 : 0; set_power[3] = dell_uart_checksum(set_power, 3); ret = dell_uart_bl_command(dell_bl, set_power, SET_CMD_LEN, resp, SET_RESP_LEN); diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index 9f51e0fcab04..e30ca325938c 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -475,7 +475,7 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) } static ssize_t data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { ssize_t ret_count = 0; @@ -492,7 +492,7 @@ static ssize_t data_read(struct file *filp, struct kobject *kobj, spin_unlock(&rbu_data.lock); return ret_count; } -static BIN_ATTR_RO(data, 0); +static const BIN_ATTR_RO(data, 0); static void callbackfn_rbu(const struct firmware *fw, void *context) { @@ -530,7 +530,7 @@ static void callbackfn_rbu(const struct firmware *fw, void *context) } static ssize_t image_type_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { int size = 0; @@ -540,7 +540,7 @@ static ssize_t image_type_read(struct file *filp, struct kobject *kobj, } static ssize_t image_type_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { int rc = count; @@ -597,10 +597,10 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj, return rc; } -static BIN_ATTR_RW(image_type, 0); +static const BIN_ATTR_RW(image_type, 0); static ssize_t packet_size_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { int size = 0; @@ -613,7 +613,7 @@ static ssize_t packet_size_read(struct file *filp, struct kobject *kobj, } static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { unsigned long temp; @@ -626,9 +626,9 @@ static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, spin_unlock(&rbu_data.lock); return count; } -static BIN_ATTR_RW(packet_size, 0); +static const BIN_ATTR_RW(packet_size, 0); -static struct bin_attribute *rbu_bin_attrs[] = { +static const struct bin_attribute *const rbu_bin_attrs[] = { &bin_attr_data, &bin_attr_image_type, &bin_attr_packet_size, @@ -636,7 +636,7 @@ static struct bin_attribute *rbu_bin_attrs[] = { }; static const struct attribute_group rbu_group = { - .bin_attrs = rbu_bin_attrs, + .bin_attrs_new = rbu_bin_attrs, }; static int __init dcdrbu_init(void) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index ae992ac1ab4a..a0eae24ca9e6 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -505,8 +505,8 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) return -ENOMEM; fujitsu_bl = priv; - strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); device->driver_data = priv; pr_info("ACPI: %s [%s]\n", @@ -891,8 +891,8 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended."); fext = device; - strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); device->driver_data = priv; /* kfifo */ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 81ccc96ffe40..6d6e13a0c6e2 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -1565,7 +1565,7 @@ static inline void omen_unregister_powersource_event_handler(void) unregister_acpi_notifier(&platform_power_source_nb); } -static int thermal_profile_setup(void) +static int thermal_profile_setup(struct platform_device *device) { int err, tp; @@ -1624,6 +1624,8 @@ static int thermal_profile_setup(void) set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); } + platform_profile_handler.name = "hp-wmi"; + platform_profile_handler.dev = &device->dev; set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); @@ -1663,7 +1665,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) if (err < 0) return err; - thermal_profile_setup(); + thermal_profile_setup(device); return 0; } @@ -1691,7 +1693,7 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device) } if (platform_profile_support) - platform_profile_remove(); + platform_profile_remove(&platform_profile_handler); } static int hp_wmi_resume_handler(struct device *device) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index e980dd18e5f6..dc98f862a06d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1041,7 +1041,7 @@ static void dytc_profile_refresh(struct ideapad_private *priv) if (profile != priv->dytc->current_profile) { priv->dytc->current_profile = profile; - platform_profile_notify(); + platform_profile_notify(&priv->dytc->pprof); } } @@ -1102,6 +1102,8 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv) mutex_init(&priv->dytc->mutex); + priv->dytc->pprof.name = "ideapad-laptop"; + priv->dytc->pprof.dev = &priv->platform_device->dev; priv->dytc->priv = priv; priv->dytc->pprof.profile_get = dytc_profile_get; priv->dytc->pprof.profile_set = dytc_profile_set; @@ -1134,7 +1136,7 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv) if (!priv->dytc) return; - platform_profile_remove(); + platform_profile_remove(&priv->dytc->pprof); mutex_destroy(&priv->dytc->mutex); kfree(priv->dytc); diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c index 8440defa6788..53af73a7fbf7 100644 --- a/drivers/platform/x86/inspur_platform_profile.c +++ b/drivers/platform/x86/inspur_platform_profile.c @@ -177,6 +177,8 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context) priv->wdev = wdev; dev_set_drvdata(&wdev->dev, priv); + priv->handler.name = "inspur-wmi"; + priv->handler.dev = &wdev->dev; priv->handler.profile_get = inspur_platform_profile_get; priv->handler.profile_set = inspur_platform_profile_set; @@ -189,7 +191,10 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context) static void inspur_wmi_remove(struct wmi_device *wdev) { - platform_profile_remove(); + struct inspur_wmi_priv *priv; + + priv = dev_get_drvdata(&wdev->dev); + platform_profile_remove(&priv->handler); } static const struct wmi_device_id inspur_wmi_id_table[] = { diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index eb698dcb9af9..19a2246f2770 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -83,6 +83,7 @@ config INTEL_BXTWC_PMIC_TMU config INTEL_BYTCRC_PWRSRC tristate "Intel Bay Trail Crystal Cove power source driver" depends on INTEL_SOC_PMIC + depends on POWER_SUPPLY help This option adds a power source driver for Crystal Cove PMICs on Intel Bay Trail devices. diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c index 3edc2a9dab38..68ac040082df 100644 --- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c +++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c @@ -8,13 +8,22 @@ * Copyright (C) 2013 Intel Corporation */ +#include +#include #include +#include #include #include #include +#include +#include #include +#define CRYSTALCOVE_PWRSRC_IRQ 0x03 #define CRYSTALCOVE_SPWRSRC_REG 0x1E +#define CRYSTALCOVE_SPWRSRC_USB BIT(0) +#define CRYSTALCOVE_SPWRSRC_DC BIT(1) +#define CRYSTALCOVE_SPWRSRC_BATTERY BIT(2) #define CRYSTALCOVE_RESETSRC0_REG 0x20 #define CRYSTALCOVE_RESETSRC1_REG 0x21 #define CRYSTALCOVE_WAKESRC_REG 0x22 @@ -22,6 +31,7 @@ struct crc_pwrsrc_data { struct regmap *regmap; struct dentry *debug_dentry; + struct power_supply *psy; unsigned int resetsrc0; unsigned int resetsrc1; unsigned int wakesrc; @@ -118,13 +128,60 @@ static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data, return regmap_write(data->regmap, reg, *val); } +static irqreturn_t crc_pwrsrc_irq_handler(int irq, void *_data) +{ + struct crc_pwrsrc_data *data = _data; + unsigned int irq_mask; + + if (regmap_read(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, &irq_mask)) + return IRQ_NONE; + + regmap_write(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, irq_mask); + + power_supply_changed(data->psy); + return IRQ_HANDLED; +} + +static int crc_pwrsrc_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct crc_pwrsrc_data *data = power_supply_get_drvdata(psy); + unsigned int pwrsrc; + int ret; + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &pwrsrc); + if (ret) + return ret; + + val->intval = !!(pwrsrc & (CRYSTALCOVE_SPWRSRC_USB | + CRYSTALCOVE_SPWRSRC_DC)); + return 0; +} + +static const enum power_supply_property crc_pwrsrc_psy_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static const struct power_supply_desc crc_pwrsrc_psy_desc = { + .name = "crystal_cove_pwrsrc", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = crc_pwrsrc_psy_props, + .num_properties = ARRAY_SIZE(crc_pwrsrc_psy_props), + .get_property = crc_pwrsrc_psy_get_property, +}; + static int crc_pwrsrc_probe(struct platform_device *pdev) { struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; struct crc_pwrsrc_data *data; - int ret; + int irq, ret; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -149,6 +206,24 @@ static int crc_pwrsrc_probe(struct platform_device *pdev) if (ret) return ret; + if (device_property_read_bool(dev->parent, "linux,register-pwrsrc-power_supply")) { + struct power_supply_config psy_cfg = { .drv_data = data }; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + data->psy = devm_power_supply_register(dev, &crc_pwrsrc_psy_desc, &psy_cfg); + if (IS_ERR(data->psy)) + return dev_err_probe(dev, PTR_ERR(data->psy), "registering power-supply\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, + crc_pwrsrc_irq_handler, + IRQF_ONESHOT, KBUILD_MODNAME, data); + if (ret) + return dev_err_probe(dev, ret, "requesting IRQ\n"); + } + data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops); debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops); diff --git a/drivers/platform/x86/intel/int0002_vgpio.c b/drivers/platform/x86/intel/int0002_vgpio.c index 0cc80603a8a9..3b48cd7a4075 100644 --- a/drivers/platform/x86/intel/int0002_vgpio.c +++ b/drivers/platform/x86/intel/int0002_vgpio.c @@ -83,8 +83,12 @@ static void int0002_irq_ack(struct irq_data *data) static void int0002_irq_unmask(struct irq_data *data) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); u32 gpe_en_reg; + gpiochip_enable_irq(gc, hwirq); + gpe_en_reg = inl(GPE0A_EN_PORT); gpe_en_reg |= GPE0A_PME_B0_EN_BIT; outl(gpe_en_reg, GPE0A_EN_PORT); @@ -92,11 +96,15 @@ static void int0002_irq_unmask(struct irq_data *data) static void int0002_irq_mask(struct irq_data *data) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + irq_hw_number_t hwirq = irqd_to_hwirq(data); u32 gpe_en_reg; gpe_en_reg = inl(GPE0A_EN_PORT); gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT; outl(gpe_en_reg, GPE0A_EN_PORT); + + gpiochip_disable_irq(gc, hwirq); } static int int0002_irq_set_wake(struct irq_data *data, unsigned int on) @@ -140,12 +148,14 @@ static bool int0002_check_wake(void *data) return (gpe_sts_reg & GPE0A_PME_B0_STS_BIT); } -static struct irq_chip int0002_irqchip = { +static const struct irq_chip int0002_irqchip = { .name = DRV_NAME, .irq_ack = int0002_irq_ack, .irq_mask = int0002_irq_mask, .irq_unmask = int0002_irq_unmask, .irq_set_wake = int0002_irq_set_wake, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void int0002_init_irq_valid_mask(struct gpio_chip *chip, @@ -203,7 +213,7 @@ static int int0002_probe(struct platform_device *pdev) } girq = &chip->irq; - girq->chip = &int0002_irqchip; + gpio_irq_chip_set_chip(girq, &int0002_irqchip); /* This let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; girq->num_parents = 0; diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c index b3a2578e06c1..1638be8fa71e 100644 --- a/drivers/platform/x86/intel/int3472/common.c +++ b/drivers/platform/x86/intel/int3472/common.c @@ -70,6 +70,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev, return -ENODEV; } + dev_dbg(dev, "Sensor name %s\n", acpi_dev_name(sensor)); + *name_ret = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT, acpi_dev_name(sensor)); if (!*name_ret) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index d881b2cfcdfc..31015ebe20d8 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -178,11 +178,11 @@ static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polar * to create clocks and regulators via the usual frameworks. * * Return: - * * 1 - To continue the loop - * * 0 - When all resources found are handled properly. - * * -EINVAL - If the resource is not a GPIO IO resource - * * -ENODEV - If the resource has no corresponding _DSM entry - * * -Other - Errors propagated from one of the sub-functions. + * * 1 - Continue the loop without adding a copy of the resource to + * * the list passed to acpi_dev_get_resources() + * * 0 - Continue the loop after adding a copy of the resource to + * * the list passed to acpi_dev_get_resources() + * * -errno - Error, break loop */ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, void *data) @@ -220,10 +220,10 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, int3472_get_func_and_polarity(type, &func, &polarity); pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value); - if (pin != agpio->pin_table[0]) - dev_warn(int3472->dev, "%s %s pin number mismatch _DSM %d resource %d\n", - func, agpio->resource_source.string_ptr, pin, - agpio->pin_table[0]); + /* Pin field is not really used under Windows and wraps around at 8 bits */ + if (pin != (agpio->pin_table[0] & 0xff)) + dev_dbg(int3472->dev, FW_BUG "%s %s pin number mismatch _DSM %d resource %d\n", + func, agpio->resource_source.string_ptr, pin, agpio->pin_table[0]); active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value); if (!active_value) @@ -289,7 +289,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, if (ret < 0) return dev_err_probe(int3472->dev, ret, err_msg); - return ret; + /* Tell acpi_dev_get_resources() to not make a copy of the resource */ + return 1; } static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) @@ -336,6 +337,9 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) struct int3472_cldb cldb; int ret; + if (!adev) + return -ENODEV; + ret = skl_int3472_fill_cldb(adev, &cldb); if (ret) { dev_err(&pdev->dev, "Couldn't fill CLDB structure\n"); diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index 1e107fd49f82..81ac4c691963 100644 --- a/drivers/platform/x86/intel/int3472/tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -152,6 +152,9 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) int ret; int i; + if (!adev) + return -ENODEV; + n_consumers = skl_int3472_fill_clk_pdata(&client->dev, &clk_pdata); if (n_consumers < 0) return n_consumers; diff --git a/drivers/platform/x86/intel/plr_tpmi.c b/drivers/platform/x86/intel/plr_tpmi.c index 691d43c3592c..2b55347a5a93 100644 --- a/drivers/platform/x86/intel/plr_tpmi.c +++ b/drivers/platform/x86/intel/plr_tpmi.c @@ -262,7 +262,7 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia struct resource *res; struct tpmi_plr *plr; void __iomem *base; - char name[16]; + char name[17]; int err; plat_info = tpmi_get_platform_data(auxdev); diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 8ed54b7a3333..7233b654bbad 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -81,7 +81,7 @@ EXPORT_SYMBOL_NS_GPL(pmt_telem_read_mmio, "INTEL_PMT"); */ static ssize_t intel_pmt_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct intel_pmt_entry *entry = container_of(attr, @@ -308,7 +308,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, entry->pmt_bin_attr.attr.name = ns->name; entry->pmt_bin_attr.attr.mode = 0440; entry->pmt_bin_attr.mmap = intel_pmt_mmap; - entry->pmt_bin_attr.read = intel_pmt_read; + entry->pmt_bin_attr.read_new = intel_pmt_read; entry->pmt_bin_attr.size = entry->size; ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 33f33b1070fd..30d1c2caf984 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -398,8 +398,8 @@ static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count, } static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -409,11 +409,11 @@ static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC); } -static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); +static const BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -423,7 +423,7 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP); } -static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); +static const BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); static ssize_t certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv, @@ -469,7 +469,7 @@ certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv, static ssize_t state_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -477,11 +477,11 @@ state_certificate_read(struct file *filp, struct kobject *kobj, return certificate_read(SDSI_CMD_READ_STATE, 0, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); static ssize_t meter_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -489,11 +489,11 @@ meter_certificate_read(struct file *filp, struct kobject *kobj, return certificate_read(SDSI_CMD_READ_METER, 0, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); static ssize_t meter_current_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -502,11 +502,11 @@ meter_current_read(struct file *filp, struct kobject *kobj, return certificate_read(SDSI_CMD_READ_METER, CTRL_METER_ENABLE_DRAM, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG); static ssize_t registers_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -528,9 +528,9 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj, return count; } -static BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); +static const BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); -static struct bin_attribute *sdsi_bin_attrs[] = { +static const struct bin_attribute *const sdsi_bin_attrs[] = { &bin_attr_registers, &bin_attr_state_certificate, &bin_attr_meter_certificate, @@ -576,7 +576,7 @@ static struct attribute *sdsi_attrs[] = { static const struct attribute_group sdsi_group = { .attrs = sdsi_attrs, - .bin_attrs = sdsi_bin_attrs, + .bin_attrs_new = sdsi_bin_attrs, .is_bin_visible = sdsi_battr_is_visible, }; __ATTRIBUTE_GROUPS(sdsi); diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c index 8d540a1c8602..c332c7cdaff5 100644 --- a/drivers/platform/x86/quickstart.c +++ b/drivers/platform/x86/quickstart.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/platform/x86/serdev_helpers.h b/drivers/platform/x86/serdev_helpers.h index bcf3a0c356ea..57eac75805e2 100644 --- a/drivers/platform/x86/serdev_helpers.h +++ b/drivers/platform/x86/serdev_helpers.h @@ -22,32 +22,14 @@ #include static inline struct device * -get_serdev_controller(const char *serial_ctrl_hid, - const char *serial_ctrl_uid, - int serial_ctrl_port, - const char *serdev_ctrl_name) +get_serdev_controller_from_parent(struct device *ctrl_dev, + int serial_ctrl_port, + const char *serdev_ctrl_name) { - struct device *ctrl_dev, *child; - struct acpi_device *ctrl_adev; + struct device *child; char name[32]; int i; - ctrl_adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1); - if (!ctrl_adev) { - pr_err("error could not get %s/%s serial-ctrl adev\n", - serial_ctrl_hid, serial_ctrl_uid); - return ERR_PTR(-ENODEV); - } - - /* get_first_physical_node() returns a weak ref */ - ctrl_dev = get_device(acpi_get_first_physical_node(ctrl_adev)); - if (!ctrl_dev) { - pr_err("error could not get %s/%s serial-ctrl physical node\n", - serial_ctrl_hid, serial_ctrl_uid); - ctrl_dev = ERR_PTR(-ENODEV); - goto put_ctrl_adev; - } - /* Walk host -> uart-ctrl -> port -> serdev-ctrl */ for (i = 0; i < 3; i++) { switch (i) { @@ -67,14 +49,40 @@ get_serdev_controller(const char *serial_ctrl_hid, put_device(ctrl_dev); if (!child) { pr_err("error could not find '%s' device\n", name); - ctrl_dev = ERR_PTR(-ENODEV); - goto put_ctrl_adev; + return ERR_PTR(-ENODEV); } ctrl_dev = child; } -put_ctrl_adev: - acpi_dev_put(ctrl_adev); return ctrl_dev; } + +static inline struct device * +get_serdev_controller(const char *serial_ctrl_hid, + const char *serial_ctrl_uid, + int serial_ctrl_port, + const char *serdev_ctrl_name) +{ + struct acpi_device *adev; + struct device *parent; + + adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1); + if (!adev) { + pr_err("error could not get %s/%s serial-ctrl adev\n", + serial_ctrl_hid, serial_ctrl_uid ?: "*"); + return ERR_PTR(-ENODEV); + } + + /* get_first_physical_node() returns a weak ref */ + parent = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + if (!parent) { + pr_err("error could not get %s/%s serial-ctrl physical node\n", + serial_ctrl_hid, serial_ctrl_uid ?: "*"); + return ERR_PTR(-ENODEV); + } + + /* This puts our reference on parent and returns a ref on the ctrl */ + return get_serdev_controller_from_parent(parent, serial_ctrl_port, serdev_ctrl_name); +} diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 6371a9f765c1..f51662861738 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -10538,6 +10538,12 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, return err; } +static struct platform_profile_handler dytc_profile = { + .name = "thinkpad-acpi", + .profile_get = dytc_profile_get, + .profile_set = dytc_profile_set, +}; + static void dytc_profile_refresh(void) { enum platform_profile_option profile; @@ -10566,15 +10572,10 @@ static void dytc_profile_refresh(void) err = convert_dytc_to_profile(funcmode, perfmode, &profile); if (!err && profile != dytc_current_profile) { dytc_current_profile = profile; - platform_profile_notify(); + platform_profile_notify(&dytc_profile); } } -static struct platform_profile_handler dytc_profile = { - .profile_get = dytc_profile_get, - .profile_set = dytc_profile_set, -}; - static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) { int err, output; @@ -10637,6 +10638,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) dbg_printk(TPACPI_DBG_INIT, "DYTC version %d: thermal mode available\n", dytc_version); + dytc_profile.dev = &tpacpi_pdev->dev; /* Create platform_profile structure and register */ err = platform_profile_register(&dytc_profile); /* @@ -10658,7 +10660,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) static void dytc_profile_exit(void) { - platform_profile_remove(); + platform_profile_remove(&dytc_profile); } static struct ibm_struct dytc_profile_driver_data = { diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c index df6f0ae6e6c7..3e33da36da8a 100644 --- a/drivers/platform/x86/wmi-bmof.c +++ b/drivers/platform/x86/wmi-bmof.c @@ -20,66 +20,66 @@ #define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910" -struct bmof_priv { - union acpi_object *bmofdata; - struct bin_attribute bmof_bin_attr; -}; - -static ssize_t read_bmof(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, +static ssize_t bmof_read(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct bmof_priv *priv = container_of(attr, struct bmof_priv, bmof_bin_attr); + struct device *dev = kobj_to_dev(kobj); + union acpi_object *obj = dev_get_drvdata(dev); - return memory_read_from_buffer(buf, count, &off, priv->bmofdata->buffer.pointer, - priv->bmofdata->buffer.length); + return memory_read_from_buffer(buf, count, &off, obj->buffer.pointer, obj->buffer.length); } +static const BIN_ATTR_ADMIN_RO(bmof, 0); + +static const struct bin_attribute * const bmof_attrs[] = { + &bin_attr_bmof, + NULL +}; + +static size_t bmof_bin_size(struct kobject *kobj, const struct bin_attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + union acpi_object *obj = dev_get_drvdata(dev); + + return obj->buffer.length; +} + +static const struct attribute_group bmof_group = { + .bin_size = bmof_bin_size, + .bin_attrs_new = bmof_attrs, +}; + +static const struct attribute_group *bmof_groups[] = { + &bmof_group, + NULL +}; + static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) { - struct bmof_priv *priv; - int ret; + union acpi_object *obj; - priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - dev_set_drvdata(&wdev->dev, priv); - - priv->bmofdata = wmidev_block_query(wdev, 0); - if (!priv->bmofdata) { + obj = wmidev_block_query(wdev, 0); + if (!obj) { dev_err(&wdev->dev, "failed to read Binary MOF\n"); return -EIO; } - if (priv->bmofdata->type != ACPI_TYPE_BUFFER) { + if (obj->type != ACPI_TYPE_BUFFER) { dev_err(&wdev->dev, "Binary MOF is not a buffer\n"); - ret = -EIO; - goto err_free; + kfree(obj); + return -EIO; } - sysfs_bin_attr_init(&priv->bmof_bin_attr); - priv->bmof_bin_attr.attr.name = "bmof"; - priv->bmof_bin_attr.attr.mode = 0400; - priv->bmof_bin_attr.read = read_bmof; - priv->bmof_bin_attr.size = priv->bmofdata->buffer.length; - - ret = device_create_bin_file(&wdev->dev, &priv->bmof_bin_attr); - if (ret) - goto err_free; + dev_set_drvdata(&wdev->dev, obj); return 0; - - err_free: - kfree(priv->bmofdata); - return ret; } static void wmi_bmof_remove(struct wmi_device *wdev) { - struct bmof_priv *priv = dev_get_drvdata(&wdev->dev); + union acpi_object *obj = dev_get_drvdata(&wdev->dev); - device_remove_bin_file(&wdev->dev, &priv->bmof_bin_attr); - kfree(priv->bmofdata); + kfree(obj); } static const struct wmi_device_id wmi_bmof_id_table[] = { @@ -90,6 +90,7 @@ static const struct wmi_device_id wmi_bmof_id_table[] = { static struct wmi_driver wmi_bmof_driver = { .driver = { .name = "wmi-bmof", + .dev_groups = bmof_groups, }, .probe = wmi_bmof_probe, .remove = wmi_bmof_remove, diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index 41ece5a37137..313be30548bc 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -3,7 +3,7 @@ # X86 Android tablet support Makefile # +obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o - x86-android-tablets-y := core.o dmi.o shared-psy-info.o \ asus.o lenovo.o other.o diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c index 07fbeab2319a..7dde63b9943f 100644 --- a/drivers/platform/x86/x86-android-tablets/asus.c +++ b/drivers/platform/x86/x86-android-tablets/asus.c @@ -145,8 +145,8 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { { - .ctrl_hid = "80860F0A", - .ctrl_uid = "2", + .ctrl.acpi.hid = "80860F0A", + .ctrl.acpi.uid = "2", .ctrl_devname = "serial0", .serdev_hid = "BCM2E3A", }, diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 4218afcec0e9..2a9c47178505 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -157,7 +157,7 @@ static struct gpiod_lookup_table * const *gpiod_lookup_tables; static const struct software_node *bat_swnode; static void (*exit_handler)(void); -static struct i2c_adapter * +static __init struct i2c_adapter * get_i2c_adap_by_handle(const struct x86_i2c_client_info *client_info) { acpi_handle handle; @@ -177,7 +177,7 @@ static __init int match_parent(struct device *dev, const void *data) return dev->parent == data; } -static struct i2c_adapter * +static __init struct i2c_adapter * get_i2c_adap_by_pci_parent(const struct x86_i2c_client_info *client_info) { struct i2c_adapter *adap = NULL; @@ -212,7 +212,7 @@ static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info if (board_info.irq < 0) return board_info.irq; - if (dev_info->use_pci_devname) + if (dev_info->use_pci) adap = get_i2c_adap_by_pci_parent(client_info); else adap = get_i2c_adap_by_handle(client_info); @@ -271,15 +271,32 @@ static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, i return 0; } -static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) +static __init struct device * +get_serdev_controller_by_pci_parent(const struct x86_serdev_info *info) { + struct pci_dev *pdev; + + pdev = pci_get_domain_bus_and_slot(0, 0, info->ctrl.pci.devfn); + if (!pdev) + return ERR_PTR(-EPROBE_DEFER); + + /* This puts our reference on pdev and returns a ref on the ctrl */ + return get_serdev_controller_from_parent(&pdev->dev, 0, info->ctrl_devname); +} + +static __init int x86_instantiate_serdev(const struct x86_dev_info *dev_info, int idx) +{ + const struct x86_serdev_info *info = &dev_info->serdev_info[idx]; struct acpi_device *serdev_adev; struct serdev_device *serdev; struct device *ctrl_dev; int ret = -ENODEV; - ctrl_dev = get_serdev_controller(info->ctrl_hid, info->ctrl_uid, 0, - info->ctrl_devname); + if (dev_info->use_pci) + ctrl_dev = get_serdev_controller_by_pci_parent(info); + else + ctrl_dev = get_serdev_controller(info->ctrl.acpi.hid, info->ctrl.acpi.uid, + 0, info->ctrl_devname); if (IS_ERR(ctrl_dev)) return PTR_ERR(ctrl_dev); @@ -446,7 +463,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) serdev_count = dev_info->serdev_count; for (i = 0; i < serdev_count; i++) { - ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); + ret = x86_instantiate_serdev(dev_info, i); if (ret < 0) { x86_android_tablet_remove(pdev); return ret; diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index ae087f1471c1..1241a97cda39 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -178,8 +178,8 @@ static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = { */ static const struct x86_serdev_info lenovo_yb1_x90_serdevs[] __initconst = { { - .ctrl_hid = "8086228A", - .ctrl_uid = "1", + .ctrl.acpi.hid = "8086228A", + .ctrl.acpi.uid = "1", .ctrl_devname = "serial0", .serdev_hid = "BCM2E1A", }, @@ -601,7 +601,7 @@ static const struct regulator_init_data lenovo_yoga_tab2_1380_bq24190_vbus_init_ .num_consumer_supplies = 1, }; -struct bq24190_platform_data lenovo_yoga_tab2_1380_bq24190_pdata = { +static struct bq24190_platform_data lenovo_yoga_tab2_1380_bq24190_pdata = { .regulator_init_data = &lenovo_yoga_tab2_1380_bq24190_vbus_init_data, }; @@ -726,7 +726,7 @@ static const struct platform_device_info lenovo_yoga_tab2_1380_pdevs[] __initcon }, }; -const char * const lenovo_yoga_tab2_1380_modules[] __initconst = { +static const char * const lenovo_yoga_tab2_1380_modules[] __initconst = { "bq24190_charger", /* For the Vbus regulator for lc824206xa */ NULL }; diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index 735df818f76b..1d93d9edb23f 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -602,14 +602,14 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { * Vexia EDU ATLA 10 tablet, Android 4.2 / 4.4 + Guadalinex Ubuntu tablet * distributed to schools in the Spanish Andalucía region. */ -const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" }; +static const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" }; static const struct property_entry vexia_edu_atla10_ulpmc_props[] = { PROPERTY_ENTRY_STRING_ARRAY("supplied-from", crystal_cove_pwrsrc_psy), { } }; -const struct software_node vexia_edu_atla10_ulpmc_node = { +static const struct software_node vexia_edu_atla10_ulpmc_node = { .properties = vexia_edu_atla10_ulpmc_props, }; @@ -715,6 +715,14 @@ static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initcon } }; +static const struct x86_serdev_info vexia_edu_atla10_serdevs[] __initconst = { + { + .ctrl.pci.devfn = PCI_DEVFN(0x1e, 3), + .ctrl_devname = "serial0", + .serdev_hid = "OBDA8723", + }, +}; + static struct gpiod_lookup_table vexia_edu_atla10_ft5416_gpios = { .dev_id = "i2c-FTSC1000", .table = { @@ -755,9 +763,11 @@ static int __init vexia_edu_atla10_init(struct device *dev) const struct x86_dev_info vexia_edu_atla10_info __initconst = { .i2c_client_info = vexia_edu_atla10_i2c_clients, .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_i2c_clients), + .serdev_info = vexia_edu_atla10_serdevs, + .serdev_count = ARRAY_SIZE(vexia_edu_atla10_serdevs), .gpiod_lookup_tables = vexia_edu_atla10_gpios, .init = vexia_edu_atla10_init, - .use_pci_devname = true, + .use_pci = true, }; /* diff --git a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c new file mode 100644 index 000000000000..5d02af1c5aaa --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * power_supply class (battery) driver for the I2C attached embedded controller + * found on Vexia EDU ATLA 10 (9V version) tablets. + * + * This is based on the ACPI Battery device in the DSDT which should work + * expect that it expects the I2C controller to be enumerated as an ACPI + * device and the tablet's BIOS enumerates all LPSS devices as PCI devices + * (and changing the LPSS BIOS settings from PCI -> ACPI does not work). + * + * Copyright (c) 2024 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* State field uses ACPI Battery spec status bits */ +#define ACPI_BATTERY_STATE_DISCHARGING BIT(0) +#define ACPI_BATTERY_STATE_CHARGING BIT(1) + +#define ATLA10_EC_BATTERY_STATE_COMMAND 0x87 +#define ATLA10_EC_BATTERY_INFO_COMMAND 0x88 + +/* From broken ACPI battery device in DSDT */ +#define ATLA10_EC_VOLTAGE_MIN_DESIGN_uV 3750000 + +/* Update data every 5 seconds */ +#define UPDATE_INTERVAL_JIFFIES (5 * HZ) + +struct atla10_ec_battery_state { + u8 status; /* Using ACPI Battery spec status bits */ + u8 capacity; /* Percent */ + __le16 charge_now_mAh; + __le16 voltage_now_mV; + __le16 current_now_mA; + __le16 charge_full_mAh; + __le16 temp; /* centi degrees Celsius */ +} __packed; + +struct atla10_ec_battery_info { + __le16 charge_full_design_mAh; + __le16 voltage_now_mV; /* Should be design voltage, but is not ? */ + __le16 charge_full_design2_mAh; +} __packed; + +struct atla10_ec_data { + struct i2c_client *client; + struct power_supply *psy; + struct delayed_work work; + struct mutex update_lock; + struct atla10_ec_battery_info info; + struct atla10_ec_battery_state state; + bool valid; /* true if state is valid */ + unsigned long last_update; /* In jiffies */ +}; + +static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values) +{ + struct device *dev = &data->client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + ret = i2c_smbus_read_block_data(data->client, cmd, buf); + if (ret != len) { + dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret); + return -EIO; + } + + memcpy(values, buf, len); + return 0; +} + +static int atla10_ec_update(struct atla10_ec_data *data) +{ + int ret; + + if (data->valid && time_before(jiffies, data->last_update + UPDATE_INTERVAL_JIFFIES)) + return 0; + + ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_STATE_COMMAND, + sizeof(data->state), (u8 *)&data->state); + if (ret) + return ret; + + data->last_update = jiffies; + data->valid = true; + return 0; +} + +static int atla10_ec_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct atla10_ec_data *data = power_supply_get_drvdata(psy); + int charge_now_mAh, charge_full_mAh, ret; + + guard(mutex)(&data->update_lock); + + ret = atla10_ec_update(data); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (data->state.status & ACPI_BATTERY_STATE_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (data->state.capacity == 100) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = data->state.capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + /* + * The EC has a bug where it reports charge-full-design as + * charge-now when the battery is full. Clamp charge-now to + * charge-full to workaround this. + */ + charge_now_mAh = le16_to_cpu(data->state.charge_now_mAh); + charge_full_mAh = le16_to_cpu(data->state.charge_full_mAh); + val->intval = min(charge_now_mAh, charge_full_mAh) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = le16_to_cpu(data->state.voltage_now_mV) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = le16_to_cpu(data->state.current_now_mA) * 1000; + /* + * Documentation/ABI/testing/sysfs-class-power specifies + * negative current for discharging. + */ + if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING) + val->intval = -val->intval; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = le16_to_cpu(data->state.charge_full_mAh) * 1000; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = le16_to_cpu(data->state.temp) / 10; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = le16_to_cpu(data->info.charge_full_design_mAh) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = ATLA10_EC_VOLTAGE_MIN_DESIGN_uV; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void atla10_ec_external_power_changed_work(struct work_struct *work) +{ + struct atla10_ec_data *data = container_of(work, struct atla10_ec_data, work.work); + + dev_dbg(&data->client->dev, "External power changed\n"); + data->valid = false; + power_supply_changed(data->psy); +} + +static void atla10_ec_external_power_changed(struct power_supply *psy) +{ + struct atla10_ec_data *data = power_supply_get_drvdata(psy); + + /* After charger plug in/out wait 0.5s for things to stabilize */ + mod_delayed_work(system_wq, &data->work, HZ / 2); +} + +static const enum power_supply_property atla10_ec_psy_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + +static const struct power_supply_desc atla10_ec_psy_desc = { + .name = "atla10_ec_battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = atla10_ec_psy_props, + .num_properties = ARRAY_SIZE(atla10_ec_psy_props), + .get_property = atla10_ec_psy_get_property, + .external_power_changed = atla10_ec_external_power_changed, +}; + +static int atla10_ec_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = { }; + struct device *dev = &client->dev; + struct atla10_ec_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + psy_cfg.drv_data = data; + data->client = client; + + ret = devm_mutex_init(dev, &data->update_lock); + if (ret) + return ret; + + ret = devm_delayed_work_autocancel(dev, &data->work, + atla10_ec_external_power_changed_work); + if (ret) + return ret; + + ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_INFO_COMMAND, + sizeof(data->info), (u8 *)&data->info); + if (ret) + return ret; + + data->psy = devm_power_supply_register(dev, &atla10_ec_psy_desc, &psy_cfg); + return PTR_ERR_OR_ZERO(data->psy); +} + +static const struct i2c_device_id atla10_ec_id_table[] = { + { "vexia_atla10_ec" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table); + +static struct i2c_driver atla10_ec_driver = { + .driver = { + .name = "vexia_atla10_ec", + }, + .probe = atla10_ec_probe, + .id_table = atla10_ec_id_table, +}; +module_i2c_driver(atla10_ec_driver); + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Battery driver for Vexia EDU ATLA 10 tablet EC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index 0fc7e8cff672..63a38a0069ba 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -57,8 +57,15 @@ struct x86_spi_dev_info { }; struct x86_serdev_info { - const char *ctrl_hid; - const char *ctrl_uid; + union { + struct { + const char *hid; + const char *uid; + } acpi; + struct { + unsigned int devfn; + } pci; + } ctrl; const char *ctrl_devname; /* * ATM the serdev core only supports of or ACPI matching; and so far all @@ -91,7 +98,7 @@ struct x86_dev_info { int gpio_button_count; int (*init)(struct device *dev); void (*exit)(void); - bool use_pci_devname; + bool use_pci; }; int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h index f5492ed413f3..0682bb4c57e5 100644 --- a/include/linux/platform_profile.h +++ b/include/linux/platform_profile.h @@ -23,10 +23,15 @@ enum platform_profile_option { PLATFORM_PROFILE_BALANCED, PLATFORM_PROFILE_BALANCED_PERFORMANCE, PLATFORM_PROFILE_PERFORMANCE, + PLATFORM_PROFILE_CUSTOM, PLATFORM_PROFILE_LAST, /*must always be last */ }; struct platform_profile_handler { + const char *name; + struct device *dev; + struct device *class_dev; + int minor; unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; int (*profile_get)(struct platform_profile_handler *pprof, enum platform_profile_option *profile); @@ -35,8 +40,8 @@ struct platform_profile_handler { }; int platform_profile_register(struct platform_profile_handler *pprof); -int platform_profile_remove(void); +int platform_profile_remove(struct platform_profile_handler *pprof); int platform_profile_cycle(void); -void platform_profile_notify(void); +void platform_profile_notify(struct platform_profile_handler *pprof); #endif /*_PLATFORM_PROFILE_H_*/