mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 00:32:00 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
This commit is contained in:
commit
971c813ec9
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -1133,7 +1133,7 @@ F: include/linux/pds/
|
||||
AMD PMC DRIVER
|
||||
M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: drivers/platform/x86/amd/pmc/
|
||||
|
||||
AMD PMF DRIVER
|
||||
|
@ -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) */
|
||||
|
@ -5,11 +5,11 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
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 <markpearson@lenovo.com>");
|
||||
MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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 +
|
||||
|
@ -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[] = {
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <acpi/video.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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, \
|
||||
}
|
||||
|
@ -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
|
||||
|
332
drivers/platform/x86/amd/pmc/mp1_stb.c
Normal file
332
drivers/platform/x86/amd/pmc/mp1_stb.c
Normal file
@ -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 <Shyam-sundar.S-k@amd.com>
|
||||
* Sanket Goswami <Sanket.Goswami@amd.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/amd_nb.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <asm/amd_nb.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/debugfs.h>
|
||||
@ -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);
|
||||
|
@ -14,6 +14,11 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
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 */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 <mario.limonciello@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
207
drivers/platform/x86/dell/dell-lis3lv02d.c
Normal file
207
drivers/platform/x86/dell/dell-lis3lv02d.c
Normal file
@ -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 <hansg@kernel.org>
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/device/bus.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#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 <hansg@kernel.org>");
|
||||
MODULE_LICENSE("GPL");
|
@ -18,10 +18,13 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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)
|
||||
|
27
drivers/platform/x86/dell/dell-smo8800-ids.h
Normal file
27
drivers/platform/x86/dell/dell-smo8800-ids.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* ACPI SMO88XX lis3lv02d freefall / accelerometer device-ids.
|
||||
*
|
||||
* Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
|
||||
* Copyright (C) 2014 Pali Rohár <pali@kernel.org>
|
||||
*/
|
||||
#ifndef _DELL_SMO8800_IDS_H_
|
||||
#define _DELL_SMO8800_IDS_H_
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static const struct acpi_device_id smo8800_ids[] = {
|
||||
{ "SMO8800" },
|
||||
{ "SMO8801" },
|
||||
{ "SMO8810" },
|
||||
{ "SMO8811" },
|
||||
{ "SMO8820" },
|
||||
{ "SMO8821" },
|
||||
{ "SMO8830" },
|
||||
{ "SMO8831" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, smo8800_ids);
|
||||
|
||||
#endif
|
@ -14,10 +14,10 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#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,
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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.
|
||||
|
@ -8,13 +8,22 @@
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
@ -22,32 +22,14 @@
|
||||
#include <linux/string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
261
drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
Normal file
261
drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
Normal file
@ -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 <hansg@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* 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 <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Battery driver for Vexia EDU ATLA 10 tablet EC");
|
||||
MODULE_LICENSE("GPL");
|
@ -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,
|
||||
|
@ -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_*/
|
||||
|
Loading…
Reference in New Issue
Block a user