platform-drivers-x86 for v6.14-1
Highlights: - acer-wmi: - Add support for PH14-51, PH16-72, and Nitro AN515-58 - Add proper hwmon support - Improve error handling when reading "gaming system info" - Replace direct EC reads for the current platform profile with WMI calls to handle EC address variations - Replace custom platform_profile cycling with the generic one - ACPI: platform_profile: Major refactoring and improvements - Support registering multiple platform_profile handlers concurrently to avoid the need to quirk which handler takes precedence - Support reporting "custom" profile for cases where the current profile is ambiguous or when settings tweaks are done outside the pre-defined profile - Abstract and layer platform_profile API better using the class_dev and drvdata - Various minor improvements - Add Documentation and kerneldoc - amd/hsmp: Add support for HSMP protocol v7 - amd/pmc: - Support AMD 1Ah family 70h - Support STB with Ryzen desktop SoCs - amd/pmf: - Support Custom BIOS inputs for PMF TA - Support passing SRA sensor data from AMD SFH (HID) to PMF TA - dell-smo8800: - Move SMO88xx quirk away from the generic i2c-i801 driver - Add accelerometer support for Dell Latitude E6330/E6430 and XPS 9550 - Support probing accelerometer for models yet to be listed in the DMI mapping table because ACPI lacks i2c-address for the accelerometer (behind a module parameter because probing might be dangerous) - HID: amd_sfh: Add support for exporting SRA sensor data - hp-wmi: Add fan and thermal support for Victus 16-s1000 - input: Add key for phone linking - input: i8042: Add context for the i8042 filter to enable cleaning up the filter related global variables from pdx86 drivers - lenovo-wmi-camera: Use SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACCESS - mellanox: mlxbf-pmc: - Add support for monitoring cycle count - Add Documentation - thinkpad_acpi: Add support for phone link key - tools/power/x86/intel-speed-select: Fix Turbo Ratio Limit restore - x86-android-tables: Add support for Vexia EDU ATLA 10 Bluetooth and EC battery driver - Miscellaneous cleanups / refactoring / improvements The following is an automated shortlog grouped by driver: acer-wmi: - add support for Acer Nitro AN515-58 - Add support for Acer PH14-51 - Add support for Acer Predator PH16-72 - Fix initialization of last_non_turbo_profile - Ignore AC events - Implement proper hwmon support - Improve error handling when reading gaming system information - Rename ACER_CAP_FAN_SPEED_READ - simplify platform profile cycling - use an ACPI bitmap to set the platform profile choices - Use devm_platform_profile_register() - use new helper function for setting overclocks - use WMI calls for platform profile handling ACPI: platform-profile: - Add a name member to handlers ACPI: platform_profile: - Add a prefix to log messages - Add choices attribute for class interface - Add concept of a "custom" profile - Add device pointer into platform profile handler - Add devm_platform_profile_register() - Add documentation - Add name attribute to class interface - Add `ops` member to handlers - Add platform handler argument to platform_profile_remove() - Add `probe` to platform_profile_ops - Add profile attribute for class interface - Allow multiple handlers - Check all profile handler to calculate next - Clean platform_profile_handler - Create class for ACPI platform profile - Let drivers set drvdata to the class device - Make sure all profile handlers agree on profile - Move matching string for new profile out of mutex - Move platform_profile_handler - Move sanity check out of the mutex - Notify change events on register and unregister - Notify class device from platform_profile_notify() - Only show profiles common for all handlers - Pass the profile handler into platform_profile_notify() - Remove platform_profile_handler from callbacks - Remove platform_profile_handler from exported symbols - Replace *class_dev member with class_dev - Use guard(mutex) for register/unregister - Use `scoped_cond_guard` alienware_wmi: - General cleanup of WMAX methods alienware-wmi: - Improve hdmi_mux, amplifier and deepslp group creation - Improve rgb-zones group creation - Modify parse_rgb() signature - Move Lighting Control State - Remove unnecessary check at module exit - Use devm_platform_profile_register() amd/hsmp: - Add support for HSMP protocol version 7 messages - Constify 'struct bin_attribute' amd/pmc: - Add STB support for AMD Desktop variants - Define enum for S2D/PMC msg_port and add helper function - Isolate STB code changes to a new file - Move STB block into amd_pmc_s2d_init() - Move STB functionality to a new file for better code organization - Update function names to align with new STB file - Update IP information structure for newer SoCs - Update S2D message id for 1Ah Family 70h model - Use ARRAY_SIZE() to fill num_ips information amd: pmc: - Use guard(mutex) amd: pmf: - Drop all quirks amd/pmf: - Enable Custom BIOS Inputs for PMF-TA - Get SRA sensor data from AMD SFH driver amd: pmf: sps: - Use devm_platform_profile_register() amd: pmf: - Switch to guard(mutex) asus-wmi: - Use devm_platform_profile_register() dell: dcdbas: - Constify 'struct bin_attribute' dell: dell-pc: - Create platform device dell-pc: - Use devm_platform_profile_register() dell_rbu: - Constify 'struct bin_attribute' dell-smo8800: - Add a couple more models to lis3lv02d_devices[] - Add support for probing for the accelerometer i2c address - Move instantiation of lis3lv02d i2c_client from i2c-i801 to dell-lis3lv02d - Move SMO88xx acpi_device_ids to dell-smo8800-ids.h dell-sysman: - Directly use firmware_attributes_class dell-uart-backlight: - Use blacklight power constant docs: platform/x86: wmi: - mention tool for invoking WMI methods Documentation/ABI: - Add document for Mellanox PMC driver - Add new sysfs field to sysfs-platform-mellanox-pmc Documentation: - Add documentation about class interface for platform profiles firmware_attributes_class: - Drop lifecycle functions - Move include linux/device/class.h - Simplify API fujitsu-laptop: - replace strcpy -> strscpy HID: amd_sfh: - Add support to export device operating states hp-bioscfg: - Directly use firmware_attributes_class hp-wmi: - Add fan and thermal profile support for Victus 16-s1000 - Use devm_platform_profile_register() ideapad-laptop: - Use devm_platform_profile_register() Input: - allocate keycode for phone linking - i8042 - Add support for platform filter contexts inspur_platform_profile: - Use devm_platform_profile_register() int3472: - Check for adev == NULL - Debug log the sensor name - Fix skl_int3472_handle_gpio_resources() return value - Make "pin number mismatch" message a debug message intel: bytcrc_pwrsrc: - fix power_supply dependency - Optionally register a power_supply dev intel: int0002_vgpio: - Make the irqchip immutable intel/pmt: - Constify 'struct bin_attribute' intel: punit_ipc: - Remove unused function intel/sdsi: - Constify 'struct bin_attribute' intel/tpmi/plr: - Make char[] longer to silence warning lenovo-wmi-camera: - Use SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACESS MAINTAINERS: - Change AMD PMC driver status to "Supported" mlxbf-bootctl: - Constify 'struct bin_attribute' - use sysfs_emit() instead of sprintf() mlxbf-pmc: - Add support for clock_measure performance block - Add support for monitoring cycle count - incorrect type in assignment mlxreg-hotplug: - use sysfs_emit() instead of sprintf() mlxreg-io: - use sysfs_emit() instead of sprintf() quickstart: - don't include 'pm_wakeup.h' directly serdev_helpers: - Add get_serdev_controller_from_parent() helper - Check for serial_ctrl_uid == NULL surface: surface_platform_profile: - Use devm_platform_profile_register() think-lmi: - Directly use firmware_attributes_class thinkpad_acpi: - Add support for new phone link hotkey thinkpad-acpi: - replace strcpy with strscpy thinkpad_acpi: - Use devm_platform_profile_register() tools/power/x86/intel-speed-select: - Fix TRL restore after SST-TF disable - v1.21 release wmi-bmof: - Make use of .bin_size() callback x86-android-tablets: - Add Bluetooth support for Vexia EDU ATLA 10 - Add missing __init to get_i2c_adap_by_*() - Add support for getting serdev-controller by PCI parent - Add Vexia EDU ATLA 10 EC battery driver - Change x86_instantiate_serdev() prototype - make platform data be static - Make variables only used locally static - Store serdev-controller ACPI HID + UID in a union Merges: - Merge branch 'fixes' into 'for-next' - Merge branch 'intel-sst' of https://github.com/spandruvada/linux-kernel into review-ilpo-next - Merge branch 'platform-drivers-x86-platform-profile' into for-next - Merge branch 'platform-drivers-x86-platform-profile' into for-next - Merge import NS conversion from 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git' into for-next -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCZ5JDNAAKCRBZrE9hU+XO MT3AAP9YSYaWZUEgV9T/De2C/ksx0XfmHULmtQHccMgqIsIxmAEAmsBOHsDozPuZ 9F2IbT4uBuQo2iwbGq0DhVd+N36kEQw= =Vz0C -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v6.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 Pull x86 platform driver updates from Ilpo Järvinen: "acer-wmi: - Add support for PH14-51, PH16-72, and Nitro AN515-58 - Add proper hwmon support - Improve error handling when reading "gaming system info" - Replace direct EC reads for the current platform profile with WMI calls to handle EC address variations - Replace custom platform_profile cycling with the generic one ACPI: - platform_profile: Major refactoring and improvements - Support registering multiple platform_profile handlers concurrently to avoid the need to quirk which handler takes precedence - Support reporting "custom" profile for cases where the current profile is ambiguous or when settings tweaks are done outside the pre-defined profile - Abstract and layer platform_profile API better using the class_dev and drvdata - Various minor improvements - Add Documentation and kerneldoc amd/hsmp: - Add support for HSMP protocol v7 amd/pmc: - Support AMD 1Ah family 70h - Support STB with Ryzen desktop SoCs amd/pmf: - Support Custom BIOS inputs for PMF TA - Support passing SRA sensor data from AMD SFH (HID) to PMF TA dell-smo8800: - Move SMO88xx quirk away from the generic i2c-i801 driver - Add accelerometer support for Dell Latitude E6330/E6430 and XPS 9550 - Support probing accelerometer for models yet to be listed in the DMI mapping table because ACPI lacks i2c-address for the accelerometer (behind a module parameter because probing might be dangerous) HID: - amd_sfh: Add support for exporting SRA sensor data hp-wmi: - Add fan and thermal support for Victus 16-s1000 input: - Add key for phone linking - i8042: Add context for the i8042 filter to enable cleaning up the filter related global variables from pdx86 drivers lenovo-wmi-camera: - Use SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACCESS mellanox mlxbf-pmc: - Add support for monitoring cycle count - Add Documentation thinkpad_acpi: - Add support for phone link key tools/power/x86/intel-speed-select: - Fix Turbo Ratio Limit restore x86-android-tables: - Add support for Vexia EDU ATLA 10 Bluetooth and EC battery driver And miscellaneous cleanups / refactoring / improvements" * tag 'platform-drivers-x86-v6.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (133 commits) platform/x86: acer-wmi: Fix initialization of last_non_turbo_profile platform/x86: acer-wmi: Ignore AC events platform/mellanox: mlxreg-io: use sysfs_emit() instead of sprintf() platform/mellanox: mlxreg-hotplug: use sysfs_emit() instead of sprintf() platform/mellanox: mlxbf-bootctl: use sysfs_emit() instead of sprintf() platform/x86: hp-wmi: Add fan and thermal profile support for Victus 16-s1000 ACPI: platform_profile: Add a prefix to log messages ACPI: platform_profile: Add documentation ACPI: platform_profile: Clean platform_profile_handler ACPI: platform_profile: Move platform_profile_handler ACPI: platform_profile: Remove platform_profile_handler from exported symbols platform/x86: thinkpad_acpi: Use devm_platform_profile_register() platform/x86: inspur_platform_profile: Use devm_platform_profile_register() platform/x86: hp-wmi: Use devm_platform_profile_register() platform/x86: ideapad-laptop: Use devm_platform_profile_register() platform/x86: dell-pc: Use devm_platform_profile_register() platform/x86: asus-wmi: Use devm_platform_profile_register() platform/x86: amd: pmf: sps: Use devm_platform_profile_register() platform/x86: acer-wmi: Use devm_platform_profile_register() platform/surface: surface_platform_profile: Use devm_platform_profile_register() ...
This commit is contained in:
commit
c9c0543b52
92 changed files with 3794 additions and 1682 deletions
Documentation
ABI/testing
userspace-api
wmi
arch/x86/include
drivers
acpi
hid/amd-sfh-hid
i2c/busses
input
platform
mellanox
surface
x86
acer-wmi.casus-nb-wmi.casus-wmi.casus-wmi.h
amd
hsmp
pmc
pmf
dell
KconfigMakefilealienware-wmi.cdcdbas.cdcdbas.hdell-laptop.cdell-lis3lv02d.cdell-pc.cdell-smo8800-ids.hdell-smo8800.cdell-uart-backlight.c
firmware_attributes_class.cfirmware_attributes_class.hfujitsu-laptop.cdell-wmi-sysman
dell_rbu.chp
ideapad-laptop.cinspur_platform_profile.cintel
lenovo-wmi-camera.cmsi-laptop.cpanasonic-laptop.cquickstart.cserdev_helpers.hthink-lmi.cthinkpad_acpi.ctoshiba_acpi.cwmi-bmof.cx86-android-tablets
include
tools/power/x86/intel-speed-select
48
Documentation/ABI/testing/sysfs-class-platform-profile
Normal file
48
Documentation/ABI/testing/sysfs-class-platform-profile
Normal file
|
@ -0,0 +1,48 @@
|
|||
What: /sys/class/platform-profile/platform-profile-X/name
|
||||
Date: March 2025
|
||||
KernelVersion: 6.14
|
||||
Description: Name of the class device given by the driver.
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/platform-profile/platform-profile-X/choices
|
||||
Date: March 2025
|
||||
KernelVersion: 6.14
|
||||
Description: This file contains a space-separated list of profiles supported
|
||||
for this device.
|
||||
|
||||
Drivers must use the following standard profile-names:
|
||||
|
||||
==================== ========================================
|
||||
low-power Low power consumption
|
||||
cool Cooler operation
|
||||
quiet Quieter operation
|
||||
balanced Balance between low power consumption
|
||||
and performance
|
||||
balanced-performance Balance between performance and low
|
||||
power consumption with a slight bias
|
||||
towards performance
|
||||
performance High performance operation
|
||||
custom Driver defined custom profile
|
||||
==================== ========================================
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/platform-profile/platform-profile-X/profile
|
||||
Date: March 2025
|
||||
KernelVersion: 6.14
|
||||
Description: Reading this file gives the current selected profile for this
|
||||
device. Writing this file with one of the strings from
|
||||
platform_profile_choices changes the profile to the new value.
|
||||
|
||||
This file can be monitored for changes by polling for POLLPRI,
|
||||
POLLPRI will be signaled on any changes, independent of those
|
||||
changes coming from a userspace write; or coming from another
|
||||
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 the driver is using a driver defined custom profile.
|
||||
|
||||
RW
|
64
Documentation/ABI/testing/sysfs-platform-mellanox-pmc
Normal file
64
Documentation/ABI/testing/sysfs-platform-mellanox-pmc
Normal file
|
@ -0,0 +1,64 @@
|
|||
HID Driver Description
|
||||
MLNXBFD0 mlxbf-pmc Performance counters (BlueField-1)
|
||||
MLNXBFD1 mlxbf-pmc Performance counters (BlueField-2)
|
||||
MLNXBFD2 mlxbf-pmc Performance counters (BlueField-3)
|
||||
|
||||
What: /sys/bus/platform/devices/<HID>/hwmon/hwmonX/<block>/event_list
|
||||
Date: Dec 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: "Shravan Kumar Ramani <shravankr@nvidia.com>"
|
||||
Description:
|
||||
List of events supported by the counters in the specific block.
|
||||
It is used to extract the event number or ID associated with
|
||||
each event.
|
||||
|
||||
What: /sys/bus/platform/devices/<HID>/hwmon/hwmonX/<block>/event<N>
|
||||
Date: Dec 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: "Shravan Kumar Ramani <shravankr@nvidia.com>"
|
||||
Description:
|
||||
Event monitored by corresponding counter. This is used to
|
||||
program or read back the event that should be or is currently
|
||||
being monitored by counter<N>.
|
||||
|
||||
What: /sys/bus/platform/devices/<HID>/hwmon/hwmonX/<block>/counter<N>
|
||||
Date: Dec 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: "Shravan Kumar Ramani <shravankr@nvidia.com>"
|
||||
Description:
|
||||
Counter value of the event being monitored. This is used to
|
||||
read the counter value of the event which was programmed using
|
||||
event<N>. This is also used to clear or reset the counter value
|
||||
by writing 0 to the counter sysfs.
|
||||
|
||||
What: /sys/bus/platform/devices/<HID>/hwmon/hwmonX/<block>/enable
|
||||
Date: Dec 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: "Shravan Kumar Ramani <shravankr@nvidia.com>"
|
||||
Description:
|
||||
Start or stop counters. This is used to start the counters
|
||||
for monitoring the programmed events and also to stop the
|
||||
counters after the desired duration. Writing value 1 will
|
||||
start all the counters in the block, and writing 0 will
|
||||
stop all the counters together.
|
||||
|
||||
What: /sys/bus/platform/devices/<HID>/hwmon/hwmonX/<block>/<reg>
|
||||
Date: Dec 2020
|
||||
KernelVersion: 5.10
|
||||
Contact: "Shravan Kumar Ramani <shravankr@nvidia.com>"
|
||||
Description:
|
||||
Value of register. This is used to read or reset the registers
|
||||
where various performance statistics are counted for each block.
|
||||
Writing 0 to the sysfs will clear the counter, writing any other
|
||||
value is not allowed.
|
||||
|
||||
What: /sys/bus/platform/devices/<HID>/hwmon/hwmonX/<block>/count_clock
|
||||
Date: Mar 2025
|
||||
KernelVersion: 6.14
|
||||
Contact: "Shravan Kumar Ramani <shravankr@nvidia.com>"
|
||||
Description:
|
||||
Use a counter for counting cycles. This is used to repurpose/dedicate
|
||||
any of the counters in the block to counting cycles. Each counter is
|
||||
represented by a bit (bit 0 for counter0, bit1 for counter1 and so on)
|
||||
and setting the corresponding bit will reserve that specific counter
|
||||
for counting cycles and override the event<N> setting.
|
|
@ -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.
|
||||
|
|
|
@ -41,6 +41,10 @@ helps in understanding how the WMI device is supposed to work. The path of the A
|
|||
method associated with a given WMI device can be retrieved using the ``lswmi`` utility
|
||||
as mentioned above.
|
||||
|
||||
If you are attempting to port a driver to Linux and are working on a Windows
|
||||
system, `WMIExplorer <https://github.com/vinaypamnani/wmie2>`_ can be useful
|
||||
for inspecting available WMI methods and invoking them directly.
|
||||
|
||||
Basic WMI driver structure
|
||||
--------------------------
|
||||
|
||||
|
|
|
@ -1148,7 +1148,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
|
||||
|
|
|
@ -80,17 +80,10 @@ typedef enum {
|
|||
|
||||
#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
|
||||
|
||||
int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
|
||||
int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);
|
||||
|
||||
#else
|
||||
|
||||
static inline int intel_punit_ipc_simple_command(int cmd,
|
||||
int para1, int para2)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
|
||||
u32 *in, u32 *out)
|
||||
{
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -2,16 +2,28 @@
|
|||
|
||||
/* Platform profile sysfs interface */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
static struct platform_profile_handler *cur_profile;
|
||||
#define to_pprof_handler(d) (container_of(d, struct platform_profile_handler, dev))
|
||||
|
||||
static DEFINE_MUTEX(profile_lock);
|
||||
|
||||
struct platform_profile_handler {
|
||||
const char *name;
|
||||
struct device dev;
|
||||
int minor;
|
||||
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
const struct platform_profile_ops *ops;
|
||||
};
|
||||
|
||||
static const char * const profile_names[] = {
|
||||
[PLATFORM_PROFILE_LOW_POWER] = "low-power",
|
||||
[PLATFORM_PROFILE_COOL] = "cool",
|
||||
|
@ -19,99 +31,373 @@ 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 = to_pprof_handler(dev);
|
||||
if (!test_bit(*bit, handler->choices))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return handler->ops->profile_set(dev, *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 = to_pprof_handler(dev);
|
||||
|
||||
lockdep_assert_held(&profile_lock);
|
||||
sysfs_notify(&handler->dev.kobj, NULL, "profile");
|
||||
kobject_uevent(&handler->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 = to_pprof_handler(dev);
|
||||
err = handler->ops->profile_get(dev, &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 = to_pprof_handler(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 = to_pprof_handler(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 void pprof_device_release(struct device *dev)
|
||||
{
|
||||
struct platform_profile_handler *pprof = to_pprof_handler(dev);
|
||||
|
||||
kfree(pprof);
|
||||
}
|
||||
|
||||
static const struct class platform_profile_class = {
|
||||
.name = "platform-profile",
|
||||
.dev_groups = profile_groups,
|
||||
.dev_release = pprof_device_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* _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 = to_pprof_handler(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,98 +410,249 @@ 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)
|
||||
/**
|
||||
* platform_profile_notify - Notify class device and legacy sysfs interface
|
||||
* @dev: The class device
|
||||
*/
|
||||
void platform_profile_notify(struct device *dev)
|
||||
{
|
||||
if (!cur_profile)
|
||||
return;
|
||||
scoped_cond_guard(mutex_intr, return, &profile_lock) {
|
||||
_notify_class_profile(dev, NULL);
|
||||
}
|
||||
sysfs_notify(acpi_kobj, NULL, "platform_profile");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_profile_notify);
|
||||
|
||||
/**
|
||||
* platform_profile_cycle - Cycles profiles available on all registered class devices
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
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);
|
||||
|
||||
int platform_profile_register(struct platform_profile_handler *pprof)
|
||||
/**
|
||||
* platform_profile_register - Creates and registers a platform profile class device
|
||||
* @dev: Parent device
|
||||
* @name: Name of the class device
|
||||
* @drvdata: Driver data that will be attached to the class device
|
||||
* @ops: Platform profile's mandatory operations
|
||||
*
|
||||
* Return: pointer to the new class device on success, ERR_PTR on failure
|
||||
*/
|
||||
struct device *platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops)
|
||||
{
|
||||
struct device *ppdev;
|
||||
int minor;
|
||||
int err;
|
||||
|
||||
mutex_lock(&profile_lock);
|
||||
/* We can only have one active profile */
|
||||
if (cur_profile) {
|
||||
mutex_unlock(&profile_lock);
|
||||
return -EEXIST;
|
||||
}
|
||||
/* Sanity check */
|
||||
if (WARN_ON_ONCE(!dev || !name || !ops || !ops->profile_get ||
|
||||
!ops->profile_set || !ops->probe))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* Sanity check the profile handler field are set */
|
||||
if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
|
||||
!pprof->profile_set || !pprof->profile_get) {
|
||||
mutex_unlock(&profile_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
struct platform_profile_handler *pprof __free(kfree) = kzalloc(
|
||||
sizeof(*pprof), GFP_KERNEL);
|
||||
if (!pprof)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = sysfs_create_group(acpi_kobj, &platform_profile_group);
|
||||
err = ops->probe(drvdata, pprof->choices);
|
||||
if (err) {
|
||||
mutex_unlock(&profile_lock);
|
||||
return err;
|
||||
dev_err(dev, "platform_profile probe failed\n");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
cur_profile = pprof;
|
||||
mutex_unlock(&profile_lock);
|
||||
return 0;
|
||||
if (bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST)) {
|
||||
dev_err(dev, "Failed to register platform_profile class device with empty choices\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
guard(mutex)(&profile_lock);
|
||||
|
||||
/* create class interface for individual handler */
|
||||
minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
|
||||
if (minor < 0)
|
||||
return ERR_PTR(minor);
|
||||
|
||||
pprof->name = name;
|
||||
pprof->ops = ops;
|
||||
pprof->minor = minor;
|
||||
pprof->dev.class = &platform_profile_class;
|
||||
pprof->dev.parent = dev;
|
||||
dev_set_drvdata(&pprof->dev, drvdata);
|
||||
dev_set_name(&pprof->dev, "platform-profile-%d", pprof->minor);
|
||||
/* device_register() takes ownership of pprof/ppdev */
|
||||
ppdev = &no_free_ptr(pprof)->dev;
|
||||
err = device_register(ppdev);
|
||||
if (err) {
|
||||
put_device(ppdev);
|
||||
goto cleanup_ida;
|
||||
}
|
||||
|
||||
sysfs_notify(acpi_kobj, NULL, "platform_profile");
|
||||
|
||||
err = sysfs_update_group(acpi_kobj, &platform_profile_group);
|
||||
if (err)
|
||||
goto cleanup_cur;
|
||||
|
||||
return ppdev;
|
||||
|
||||
cleanup_cur:
|
||||
device_unregister(ppdev);
|
||||
|
||||
cleanup_ida:
|
||||
ida_free(&platform_profile_ida, minor);
|
||||
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_profile_register);
|
||||
|
||||
int platform_profile_remove(void)
|
||||
/**
|
||||
* platform_profile_remove - Unregisters a platform profile class device
|
||||
* @dev: Class device
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
int platform_profile_remove(struct device *dev)
|
||||
{
|
||||
sysfs_remove_group(acpi_kobj, &platform_profile_group);
|
||||
struct platform_profile_handler *pprof = to_pprof_handler(dev);
|
||||
int id;
|
||||
guard(mutex)(&profile_lock);
|
||||
|
||||
id = pprof->minor;
|
||||
device_unregister(&pprof->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 void devm_platform_profile_release(struct device *dev, void *res)
|
||||
{
|
||||
struct device **ppdev = res;
|
||||
|
||||
platform_profile_remove(*ppdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_platform_profile_register - Device managed version of platform_profile_register
|
||||
* @dev: Parent device
|
||||
* @name: Name of the class device
|
||||
* @drvdata: Driver data that will be attached to the class device
|
||||
* @ops: Platform profile's mandatory operations
|
||||
*
|
||||
* Return: pointer to the new class device on success, ERR_PTR on failure
|
||||
*/
|
||||
struct device *devm_platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops)
|
||||
{
|
||||
struct device *ppdev;
|
||||
struct device **dr;
|
||||
|
||||
dr = devres_alloc(devm_platform_profile_release, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ppdev = platform_profile_register(dev, name, drvdata, ops);
|
||||
if (IS_ERR(ppdev)) {
|
||||
devres_free(dr);
|
||||
return ppdev;
|
||||
}
|
||||
|
||||
*dr = ppdev;
|
||||
devres_add(dev, dr);
|
||||
|
||||
return ppdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_platform_profile_register);
|
||||
|
||||
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");
|
||||
|
|
|
@ -43,6 +43,7 @@ struct amd_mp2_sensor_info {
|
|||
struct sfh_dev_status {
|
||||
bool is_hpd_present;
|
||||
bool is_als_present;
|
||||
bool is_sra_present;
|
||||
};
|
||||
|
||||
struct amd_mp2_dev {
|
||||
|
|
|
@ -30,6 +30,7 @@ static int amd_sfh_get_sensor_num(struct amd_mp2_dev *mp2, u8 *sensor_id)
|
|||
case ACCEL_IDX:
|
||||
case GYRO_IDX:
|
||||
case MAG_IDX:
|
||||
case SRA_IDX:
|
||||
case ALS_IDX:
|
||||
case HPD_IDX:
|
||||
if (BIT(i) & slist->sl.sensors)
|
||||
|
@ -58,6 +59,8 @@ static const char *get_sensor_name(int idx)
|
|||
return "gyroscope";
|
||||
case MAG_IDX:
|
||||
return "magnetometer";
|
||||
case SRA_IDX:
|
||||
return "SRA";
|
||||
case ALS_IDX:
|
||||
return "ALS";
|
||||
case HPD_IDX:
|
||||
|
@ -130,6 +133,23 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
|
|||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
cl_data->sensor_sts[i] = SENSOR_DISABLED;
|
||||
|
||||
if (cl_data->num_hid_devices == 1 && cl_data->sensor_idx[0] == SRA_IDX)
|
||||
break;
|
||||
|
||||
if (cl_data->sensor_idx[i] == SRA_IDX) {
|
||||
info.sensor_idx = cl_data->sensor_idx[i];
|
||||
writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0));
|
||||
mp2_ops->start(privdata, info);
|
||||
status = amd_sfh_wait_for_response
|
||||
(privdata, cl_data->sensor_idx[i], ENABLE_SENSOR);
|
||||
|
||||
cl_data->sensor_sts[i] = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED;
|
||||
if (cl_data->sensor_sts[i] == SENSOR_ENABLED)
|
||||
privdata->dev_en.is_sra_present = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
cl_data->sensor_requested_cnt[i] = 0;
|
||||
cl_data->cur_hid_dev = i;
|
||||
cl_idx = cl_data->sensor_idx[i];
|
||||
|
@ -181,6 +201,8 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
|
|||
}
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
if (cl_data->sensor_idx[i] == SRA_IDX)
|
||||
continue;
|
||||
cl_data->cur_hid_dev = i;
|
||||
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
|
||||
cl_data->is_any_sensor_enabled = true;
|
||||
|
|
|
@ -87,6 +87,41 @@ void sfh_interface_init(struct amd_mp2_dev *mp2)
|
|||
emp2 = mp2;
|
||||
}
|
||||
|
||||
static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement)
|
||||
{
|
||||
struct sfh_op_mode mode;
|
||||
|
||||
if (!platform_type || !laptop_placement)
|
||||
return -EINVAL;
|
||||
|
||||
if (!emp2 || !emp2->dev_en.is_sra_present)
|
||||
return -ENODEV;
|
||||
|
||||
mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3));
|
||||
|
||||
*platform_type = mode.op_mode.devicemode;
|
||||
|
||||
if (mode.op_mode.ontablestate == 1) {
|
||||
*laptop_placement = ON_TABLE;
|
||||
} else if (mode.op_mode.ontablestate == 2) {
|
||||
*laptop_placement = ON_LAP_MOTION;
|
||||
} else if (mode.op_mode.inbagstate == 1) {
|
||||
*laptop_placement = IN_BAG;
|
||||
} else if (mode.op_mode.outbagstate == 1) {
|
||||
*laptop_placement = OUT_OF_BAG;
|
||||
} else if (mode.op_mode.ontablestate == 0 || mode.op_mode.inbagstate == 0 ||
|
||||
mode.op_mode.outbagstate == 0) {
|
||||
*laptop_placement = LP_UNKNOWN;
|
||||
pr_warn_once("Unknown laptop placement\n");
|
||||
} else if (mode.op_mode.ontablestate == 3 || mode.op_mode.inbagstate == 3 ||
|
||||
mode.op_mode.outbagstate == 3) {
|
||||
*laptop_placement = LP_UNDEFINED;
|
||||
pr_warn_once("Undefined laptop placement\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_sfh_hpd_info(u8 *user_present)
|
||||
{
|
||||
struct hpd_status hpdstatus;
|
||||
|
@ -131,6 +166,9 @@ int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
|
|||
return amd_sfh_hpd_info(&sfh_info->user_present);
|
||||
case MT_ALS:
|
||||
return amd_sfh_als_info(&sfh_info->ambient_light);
|
||||
case MT_SRA:
|
||||
return amd_sfh_mode_info(&sfh_info->platform_type,
|
||||
&sfh_info->laptop_placement);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
|
|
|
@ -22,8 +22,9 @@ enum sensor_index {
|
|||
ACCEL_IDX,
|
||||
GYRO_IDX,
|
||||
MAG_IDX,
|
||||
ALS_IDX = 4,
|
||||
HPD_IDX = 5,
|
||||
SRA_IDX,
|
||||
ALS_IDX,
|
||||
HPD_IDX,
|
||||
MAX_IDX = 15,
|
||||
};
|
||||
|
||||
|
@ -164,6 +165,25 @@ struct hpd_status {
|
|||
};
|
||||
};
|
||||
|
||||
struct sfh_op_mode {
|
||||
union {
|
||||
u32 val;
|
||||
struct {
|
||||
u32 mode : 3;
|
||||
u32 lidstatus : 1;
|
||||
u32 angle : 10;
|
||||
u32 inbagstatedbg : 2;
|
||||
u32 ontablestate : 2;
|
||||
u32 inbagstate : 2;
|
||||
u32 outbagstate : 2;
|
||||
u32 inbagmlcstate : 1;
|
||||
u32 powerstate : 2;
|
||||
u32 data : 3;
|
||||
u32 devicemode : 4;
|
||||
} op_mode;
|
||||
};
|
||||
};
|
||||
|
||||
void sfh_interface_init(struct amd_mp2_dev *mp2);
|
||||
void sfh_deinit_emp2(void);
|
||||
void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops);
|
||||
|
|
|
@ -1162,128 +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 },
|
||||
{ "Precision M6800", 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)
|
||||
{
|
||||
|
@ -1303,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)
|
||||
|
|
|
@ -121,7 +121,7 @@ static void slidebar_mode_set(u8 mode)
|
|||
}
|
||||
|
||||
static bool slidebar_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
struct serio *port, void *context)
|
||||
{
|
||||
static bool extended = false;
|
||||
|
||||
|
@ -219,7 +219,7 @@ static int __init ideapad_probe(struct platform_device* pdev)
|
|||
input_set_capability(slidebar_input_dev, EV_ABS, ABS_X);
|
||||
input_set_abs_params(slidebar_input_dev, ABS_X, 0, 0xff, 0, 0);
|
||||
|
||||
err = i8042_install_filter(slidebar_i8042_filter);
|
||||
err = i8042_install_filter(slidebar_i8042_filter, NULL);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to install i8042 filter: %d\n", err);
|
||||
|
|
|
@ -179,8 +179,8 @@ static struct platform_device *i8042_platform_device;
|
|||
static struct notifier_block i8042_kbd_bind_notifier_block;
|
||||
|
||||
static bool i8042_handle_data(int irq);
|
||||
static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio);
|
||||
static i8042_filter_t i8042_platform_filter;
|
||||
static void *i8042_platform_filter_context;
|
||||
|
||||
void i8042_lock_chip(void)
|
||||
{
|
||||
|
@ -194,8 +194,7 @@ void i8042_unlock_chip(void)
|
|||
}
|
||||
EXPORT_SYMBOL(i8042_unlock_chip);
|
||||
|
||||
int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio))
|
||||
int i8042_install_filter(i8042_filter_t filter, void *context)
|
||||
{
|
||||
guard(spinlock_irqsave)(&i8042_lock);
|
||||
|
||||
|
@ -203,12 +202,12 @@ int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
|
|||
return -EBUSY;
|
||||
|
||||
i8042_platform_filter = filter;
|
||||
i8042_platform_filter_context = context;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(i8042_install_filter);
|
||||
|
||||
int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
|
||||
struct serio *port))
|
||||
int i8042_remove_filter(i8042_filter_t filter)
|
||||
{
|
||||
guard(spinlock_irqsave)(&i8042_lock);
|
||||
|
||||
|
@ -216,6 +215,7 @@ int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
|
|||
return -EINVAL;
|
||||
|
||||
i8042_platform_filter = NULL;
|
||||
i8042_platform_filter_context = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(i8042_remove_filter);
|
||||
|
@ -480,7 +480,10 @@ static bool i8042_filter(unsigned char data, unsigned char str,
|
|||
}
|
||||
}
|
||||
|
||||
if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) {
|
||||
if (!i8042_platform_filter)
|
||||
return false;
|
||||
|
||||
if (i8042_platform_filter(data, str, serio, i8042_platform_filter_context)) {
|
||||
dbg("Filtered out by platform filter\n");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ static ssize_t post_reset_wdog_show(struct device *dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", ret);
|
||||
return sysfs_emit(buf, "%d\n", ret);
|
||||
}
|
||||
|
||||
static ssize_t post_reset_wdog_store(struct device *dev,
|
||||
|
@ -206,7 +206,7 @@ static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
|
|||
if (action < 0)
|
||||
return action;
|
||||
|
||||
return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
|
||||
return sysfs_emit(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
|
||||
}
|
||||
|
||||
static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
|
||||
|
@ -274,14 +274,14 @@ static ssize_t lifecycle_state_show(struct device *dev,
|
|||
* due to using the test bits.
|
||||
*/
|
||||
if (test_state) {
|
||||
return sprintf(buf, "%s(test)\n",
|
||||
return sysfs_emit(buf, "%s(test)\n",
|
||||
mlxbf_bootctl_lifecycle_states[lc_state]);
|
||||
} else if (use_dev_key &&
|
||||
(lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) {
|
||||
return sprintf(buf, "Secured (development)\n");
|
||||
return sysfs_emit(buf, "Secured (development)\n");
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
|
||||
return sysfs_emit(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
|
||||
}
|
||||
|
||||
static ssize_t secure_boot_fuse_state_show(struct device *dev,
|
||||
|
@ -332,9 +332,9 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev,
|
|||
else
|
||||
status = valid ? "Invalid" : "Free";
|
||||
}
|
||||
buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
|
||||
buf_len += sysfs_emit(buf + buf_len, "%d:%s ", key, status);
|
||||
}
|
||||
buf_len += sprintf(buf + buf_len, "\n");
|
||||
buf_len += sysfs_emit(buf + buf_len, "\n");
|
||||
|
||||
return buf_len;
|
||||
}
|
||||
|
@ -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,
|
||||
|
|
|
@ -88,6 +88,7 @@
|
|||
#define MLXBF_PMC_CRSPACE_PERFMON_CTL(n) (n * MLXBF_PMC_CRSPACE_PERFMON_REG0_SZ)
|
||||
#define MLXBF_PMC_CRSPACE_PERFMON_EN BIT(30)
|
||||
#define MLXBF_PMC_CRSPACE_PERFMON_CLR BIT(28)
|
||||
#define MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0x4)
|
||||
#define MLXBF_PMC_CRSPACE_PERFMON_VAL0(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0xc)
|
||||
|
||||
/**
|
||||
|
@ -114,6 +115,7 @@ struct mlxbf_pmc_attribute {
|
|||
* @attr_event: Attributes for "event" sysfs files
|
||||
* @attr_event_list: Attributes for "event_list" sysfs files
|
||||
* @attr_enable: Attributes for "enable" sysfs files
|
||||
* @attr_count_clock: Attributes for "count_clock" sysfs files
|
||||
* @block_attr: All attributes needed for the block
|
||||
* @block_attr_grp: Attribute group for the block
|
||||
*/
|
||||
|
@ -126,6 +128,7 @@ struct mlxbf_pmc_block_info {
|
|||
struct mlxbf_pmc_attribute *attr_event;
|
||||
struct mlxbf_pmc_attribute attr_event_list;
|
||||
struct mlxbf_pmc_attribute attr_enable;
|
||||
struct mlxbf_pmc_attribute attr_count_clock;
|
||||
struct attribute *block_attr[MLXBF_PMC_MAX_ATTRS];
|
||||
struct attribute_group block_attr_grp;
|
||||
};
|
||||
|
@ -859,6 +862,37 @@ static const struct mlxbf_pmc_events mlxbf_pmc_llt_miss_events[] = {
|
|||
{75, "HISTOGRAM_HISTOGRAM_BIN9"},
|
||||
};
|
||||
|
||||
static const struct mlxbf_pmc_events mlxbf_pmc_clock_events[] = {
|
||||
{ 0x0, "FMON_CLK_LAST_COUNT_PLL_D1_INST0" },
|
||||
{ 0x4, "REFERENCE_WINDOW_WIDTH_PLL_D1_INST0" },
|
||||
{ 0x8, "FMON_CLK_LAST_COUNT_PLL_D1_INST1" },
|
||||
{ 0xc, "REFERENCE_WINDOW_WIDTH_PLL_D1_INST1" },
|
||||
{ 0x10, "FMON_CLK_LAST_COUNT_PLL_G1" },
|
||||
{ 0x14, "REFERENCE_WINDOW_WIDTH_PLL_G1" },
|
||||
{ 0x18, "FMON_CLK_LAST_COUNT_PLL_W1" },
|
||||
{ 0x1c, "REFERENCE_WINDOW_WIDTH_PLL_W1" },
|
||||
{ 0x20, "FMON_CLK_LAST_COUNT_PLL_T1" },
|
||||
{ 0x24, "REFERENCE_WINDOW_WIDTH_PLL_T1" },
|
||||
{ 0x28, "FMON_CLK_LAST_COUNT_PLL_A0" },
|
||||
{ 0x2c, "REFERENCE_WINDOW_WIDTH_PLL_A0" },
|
||||
{ 0x30, "FMON_CLK_LAST_COUNT_PLL_C0" },
|
||||
{ 0x34, "REFERENCE_WINDOW_WIDTH_PLL_C0" },
|
||||
{ 0x38, "FMON_CLK_LAST_COUNT_PLL_N1" },
|
||||
{ 0x3c, "REFERENCE_WINDOW_WIDTH_PLL_N1" },
|
||||
{ 0x40, "FMON_CLK_LAST_COUNT_PLL_I1" },
|
||||
{ 0x44, "REFERENCE_WINDOW_WIDTH_PLL_I1" },
|
||||
{ 0x48, "FMON_CLK_LAST_COUNT_PLL_R1" },
|
||||
{ 0x4c, "REFERENCE_WINDOW_WIDTH_PLL_R1" },
|
||||
{ 0x50, "FMON_CLK_LAST_COUNT_PLL_P1" },
|
||||
{ 0x54, "REFERENCE_WINDOW_WIDTH_PLL_P1" },
|
||||
{ 0x58, "FMON_CLK_LAST_COUNT_REF_100_INST0" },
|
||||
{ 0x5c, "REFERENCE_WINDOW_WIDTH_REF_100_INST0" },
|
||||
{ 0x60, "FMON_CLK_LAST_COUNT_REF_100_INST1" },
|
||||
{ 0x64, "REFERENCE_WINDOW_WIDTH_REF_100_INST1" },
|
||||
{ 0x68, "FMON_CLK_LAST_COUNT_REF_156" },
|
||||
{ 0x6c, "REFERENCE_WINDOW_WIDTH_REF_156" },
|
||||
};
|
||||
|
||||
static struct mlxbf_pmc_context *pmc;
|
||||
|
||||
/* UUID used to probe ATF service. */
|
||||
|
@ -1032,6 +1066,9 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size
|
|||
} else if (strstr(blk, "llt")) {
|
||||
events = mlxbf_pmc_llt_events;
|
||||
size = ARRAY_SIZE(mlxbf_pmc_llt_events);
|
||||
} else if (strstr(blk, "clock_measure")) {
|
||||
events = mlxbf_pmc_clock_events;
|
||||
size = ARRAY_SIZE(mlxbf_pmc_clock_events);
|
||||
} else {
|
||||
events = NULL;
|
||||
size = 0;
|
||||
|
@ -1168,7 +1205,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 +1229,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 +1442,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 +
|
||||
|
@ -1466,14 +1503,15 @@ static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u
|
|||
/* Method to read a register */
|
||||
static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
|
||||
{
|
||||
u32 ecc_out;
|
||||
u32 reg;
|
||||
|
||||
if (strstr(pmc->block_name[blk_num], "ecc")) {
|
||||
if ((strstr(pmc->block_name[blk_num], "ecc")) ||
|
||||
(strstr(pmc->block_name[blk_num], "clock_measure"))) {
|
||||
if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset,
|
||||
&ecc_out))
|
||||
®))
|
||||
return -EFAULT;
|
||||
|
||||
*result = ecc_out;
|
||||
*result = reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1487,6 +1525,9 @@ static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
|
|||
/* Method to write to a register */
|
||||
static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data)
|
||||
{
|
||||
if (strstr(pmc->block_name[blk_num], "clock_measure"))
|
||||
return -EINVAL;
|
||||
|
||||
if (strstr(pmc->block_name[blk_num], "ecc")) {
|
||||
return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset,
|
||||
MLXBF_PMC_WRITE_REG_32, data);
|
||||
|
@ -1763,6 +1804,49 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
/* Show function for "count_clock" sysfs files - only for crspace */
|
||||
static ssize_t mlxbf_pmc_count_clock_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_count_clock = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
unsigned int blk_num;
|
||||
u32 reg;
|
||||
|
||||
blk_num = attr_count_clock->nr;
|
||||
|
||||
if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
|
||||
MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(pmc->block[blk_num].counters),
|
||||
®))
|
||||
return -EINVAL;
|
||||
|
||||
return sysfs_emit(buf, "%u\n", reg);
|
||||
}
|
||||
|
||||
/* Store function for "count_clock" sysfs files - only for crspace */
|
||||
static ssize_t mlxbf_pmc_count_clock_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_count_clock = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
unsigned int blk_num;
|
||||
u32 reg;
|
||||
int err;
|
||||
|
||||
blk_num = attr_count_clock->nr;
|
||||
|
||||
err = kstrtouint(buf, 0, ®);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mlxbf_pmc_write(pmc->block[blk_num].mmio_base +
|
||||
MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(pmc->block[blk_num].counters),
|
||||
MLXBF_PMC_WRITE_REG_32, reg);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Populate attributes for blocks with counters to monitor performance */
|
||||
static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num)
|
||||
{
|
||||
|
@ -1801,6 +1885,21 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_
|
|||
attr = NULL;
|
||||
}
|
||||
|
||||
if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) {
|
||||
/* Program crspace counters to count clock cycles using "count_clock" sysfs */
|
||||
attr = &pmc->block[blk_num].attr_count_clock;
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = mlxbf_pmc_count_clock_show;
|
||||
attr->dev_attr.store = mlxbf_pmc_count_clock_store;
|
||||
attr->nr = blk_num;
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"count_clock");
|
||||
if (!attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
|
||||
attr = NULL;
|
||||
}
|
||||
|
||||
pmc->block[blk_num].attr_counter = devm_kcalloc(
|
||||
dev, pmc->block[blk_num].counters,
|
||||
sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
|
||||
|
|
|
@ -232,7 +232,7 @@ static ssize_t mlxreg_hotplug_attr_show(struct device *dev,
|
|||
regval = !!(regval & data->mask);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%u\n", regval);
|
||||
return sysfs_emit(buf, "%u\n", regval);
|
||||
}
|
||||
|
||||
#define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
|
||||
|
|
|
@ -126,7 +126,7 @@ mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
mutex_unlock(&priv->io_lock);
|
||||
|
||||
return sprintf(buf, "%u\n", regval);
|
||||
return sysfs_emit(buf, "%u\n", regval);
|
||||
|
||||
access_error:
|
||||
mutex_unlock(&priv->io_lock);
|
||||
|
|
|
@ -40,7 +40,7 @@ struct ssam_tmp_profile_info {
|
|||
|
||||
struct ssam_platform_profile_device {
|
||||
struct ssam_device *sdev;
|
||||
struct platform_profile_handler handler;
|
||||
struct device *ppdev;
|
||||
bool has_fan;
|
||||
};
|
||||
|
||||
|
@ -154,14 +154,14 @@ static int convert_profile_to_ssam_fan(struct ssam_device *sdev, enum platform_p
|
|||
}
|
||||
}
|
||||
|
||||
static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
static int ssam_platform_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct ssam_platform_profile_device *tpd;
|
||||
enum ssam_tmp_profile tp;
|
||||
int status;
|
||||
|
||||
tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
|
||||
tpd = dev_get_drvdata(dev);
|
||||
|
||||
status = ssam_tmp_profile_get(tpd->sdev, &tp);
|
||||
if (status)
|
||||
|
@ -175,13 +175,13 @@ static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
static int ssam_platform_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct ssam_platform_profile_device *tpd;
|
||||
int tp;
|
||||
|
||||
tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
|
||||
tpd = dev_get_drvdata(dev);
|
||||
|
||||
tp = convert_profile_to_ssam_tmp(tpd->sdev, profile);
|
||||
if (tp < 0)
|
||||
|
@ -201,6 +201,22 @@ static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
|
|||
return tp;
|
||||
}
|
||||
|
||||
static int ssam_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops ssam_platform_profile_ops = {
|
||||
.probe = ssam_platform_profile_probe,
|
||||
.profile_get = ssam_platform_profile_get,
|
||||
.profile_set = ssam_platform_profile_set,
|
||||
};
|
||||
|
||||
static int surface_platform_profile_probe(struct ssam_device *sdev)
|
||||
{
|
||||
struct ssam_platform_profile_device *tpd;
|
||||
|
@ -210,23 +226,14 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
|
|||
return -ENOMEM;
|
||||
|
||||
tpd->sdev = sdev;
|
||||
|
||||
tpd->handler.profile_get = ssam_platform_profile_get;
|
||||
tpd->handler.profile_set = ssam_platform_profile_set;
|
||||
ssam_device_set_drvdata(sdev, tpd);
|
||||
|
||||
tpd->has_fan = device_property_read_bool(&sdev->dev, "has_fan");
|
||||
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices);
|
||||
tpd->ppdev = devm_platform_profile_register(&sdev->dev, "Surface Platform Profile",
|
||||
tpd, &ssam_platform_profile_ops);
|
||||
|
||||
return platform_profile_register(&tpd->handler);
|
||||
}
|
||||
|
||||
static void surface_platform_profile_remove(struct ssam_device *sdev)
|
||||
{
|
||||
platform_profile_remove();
|
||||
return PTR_ERR_OR_ZERO(tpd->ppdev);
|
||||
}
|
||||
|
||||
static const struct ssam_device_id ssam_platform_profile_match[] = {
|
||||
|
@ -237,7 +244,6 @@ MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);
|
|||
|
||||
static struct ssam_device_driver surface_platform_profile = {
|
||||
.probe = surface_platform_profile_probe,
|
||||
.remove = surface_platform_profile_remove,
|
||||
.match_table = ssam_platform_profile_match,
|
||||
.driver = {
|
||||
.name = "surface_platform_profile",
|
||||
|
|
|
@ -30,7 +30,10 @@
|
|||
#include <linux/input/sparse-keymap.h>
|
||||
#include <acpi/video.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
MODULE_AUTHOR("Carlos Corbacho");
|
||||
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
|
||||
|
@ -67,10 +70,16 @@ MODULE_LICENSE("GPL");
|
|||
#define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5
|
||||
#define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14
|
||||
#define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22
|
||||
#define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23
|
||||
|
||||
#define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54
|
||||
#define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0)
|
||||
#define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0)
|
||||
#define ACER_GAMING_MISC_SETTING_VALUE_MASK GENMASK_ULL(15, 8)
|
||||
|
||||
#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
|
||||
|
@ -95,12 +104,33 @@ enum acer_wmi_event_ids {
|
|||
WMID_HOTKEY_EVENT = 0x1,
|
||||
WMID_ACCEL_OR_KBD_DOCK_EVENT = 0x5,
|
||||
WMID_GAMING_TURBO_KEY_EVENT = 0x7,
|
||||
WMID_AC_EVENT = 0x8,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
enum acer_wmi_predator_v4_oc {
|
||||
ACER_WMID_OC_NORMAL = 0x0000,
|
||||
ACER_WMID_OC_TURBO = 0x0002,
|
||||
};
|
||||
|
||||
enum acer_wmi_gaming_misc_setting {
|
||||
ACER_WMID_MISC_SETTING_OC_1 = 0x0005,
|
||||
ACER_WMID_MISC_SETTING_OC_2 = 0x0007,
|
||||
ACER_WMID_MISC_SETTING_SUPPORTED_PROFILES = 0x000A,
|
||||
ACER_WMID_MISC_SETTING_PLATFORM_PROFILE = 0x000B,
|
||||
};
|
||||
|
||||
static const struct key_entry acer_wmi_keymap[] __initconst = {
|
||||
|
@ -246,7 +276,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 +301,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 +389,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 +424,20 @@ static struct quirk_entry quirk_acer_predator_ph315_53 = {
|
|||
.gpu_fans = 1,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_acer_predator_ph16_72 = {
|
||||
.turbo = 1,
|
||||
.cpu_fans = 1,
|
||||
.gpu_fans = 1,
|
||||
.predator_v4 = 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,
|
||||
};
|
||||
|
@ -564,6 +609,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
|
|||
},
|
||||
.driver_data = &quirk_acer_travelmate_2490,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Acer Nitro AN515-58",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Nitro AN515-58"),
|
||||
},
|
||||
.driver_data = &quirk_acer_predator_v4,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Acer Predator PH315-53",
|
||||
|
@ -591,6 +645,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
|
|||
},
|
||||
.driver_data = &quirk_acer_predator_v4,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Acer Predator PH16-72",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Predator PH16-72"),
|
||||
},
|
||||
.driver_data = &quirk_acer_predator_ph16_72,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Acer Predator PH18-71",
|
||||
|
@ -600,6 +663,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",
|
||||
|
@ -713,29 +785,24 @@ static const struct dmi_system_id non_acer_quirks[] __initconst = {
|
|||
{}
|
||||
};
|
||||
|
||||
static struct platform_profile_handler platform_profile_handler;
|
||||
static struct device *platform_profile_device;
|
||||
static bool platform_profile_support;
|
||||
|
||||
/*
|
||||
* The profile used before turbo mode. This variable is needed for
|
||||
* returning from turbo mode when the mode key is in toggle mode.
|
||||
*/
|
||||
static int last_non_turbo_profile;
|
||||
static int last_non_turbo_profile = INT_MIN;
|
||||
|
||||
enum acer_predator_v4_thermal_profile_ec {
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x04,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x03,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x02,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x01,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x00,
|
||||
};
|
||||
/* The most performant supported profile */
|
||||
static int acer_predator_v4_max_perf;
|
||||
|
||||
enum acer_predator_v4_thermal_profile_wmi {
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI = 0x060B,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI = 0x050B,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI = 0x040B,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI = 0x0B,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI = 0x010B,
|
||||
enum acer_predator_v4_thermal_profile {
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x00,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x01,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x04,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x05,
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x06,
|
||||
};
|
||||
|
||||
/* Find which quirks are needed for a particular vendor/ model pair */
|
||||
|
@ -1448,6 +1515,45 @@ WMI_gaming_execute_u64(u32 method_id, u64 in, u64 *out)
|
|||
return status;
|
||||
}
|
||||
|
||||
static int WMI_gaming_execute_u32_u64(u32 method_id, u32 in, u64 *out)
|
||||
{
|
||||
struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_buffer input = {
|
||||
.length = sizeof(in),
|
||||
.pointer = &in,
|
||||
};
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int ret = 0;
|
||||
|
||||
status = wmi_evaluate_method(WMID_GUID4, 0, method_id, &input, &result);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
obj = result.pointer;
|
||||
if (obj && out) {
|
||||
switch (obj->type) {
|
||||
case ACPI_TYPE_INTEGER:
|
||||
*out = obj->integer.value;
|
||||
break;
|
||||
case ACPI_TYPE_BUFFER:
|
||||
if (obj->buffer.length < sizeof(*out))
|
||||
ret = -ENOMSG;
|
||||
else
|
||||
*out = get_unaligned_le64(obj->buffer.pointer);
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -ENOMSG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
|
||||
{
|
||||
u32 method_id = 0;
|
||||
|
@ -1462,9 +1568,6 @@ static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
|
|||
case ACER_CAP_TURBO_FAN:
|
||||
method_id = ACER_WMID_SET_GAMING_FAN_BEHAVIOR;
|
||||
break;
|
||||
case ACER_CAP_TURBO_OC:
|
||||
method_id = ACER_WMID_SET_GAMING_MISC_SETTING_METHODID;
|
||||
break;
|
||||
default:
|
||||
return AE_BAD_PARAMETER;
|
||||
}
|
||||
|
@ -1497,6 +1600,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*/
|
||||
|
@ -1518,6 +1639,48 @@ static void WMID_gaming_set_fan_mode(u8 fan_mode)
|
|||
WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN);
|
||||
}
|
||||
|
||||
static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value)
|
||||
{
|
||||
acpi_status status;
|
||||
u64 input = 0;
|
||||
u64 result;
|
||||
|
||||
input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting);
|
||||
input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_VALUE_MASK, value);
|
||||
|
||||
status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, input, &result);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
/* The return status must be zero for the operation to have succeeded */
|
||||
if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WMID_gaming_get_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 *value)
|
||||
{
|
||||
u64 input = 0;
|
||||
u64 result;
|
||||
int ret;
|
||||
|
||||
input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting);
|
||||
|
||||
ret = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_MISC_SETTING_METHODID, input,
|
||||
&result);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* The return status must be zero for the operation to have succeeded */
|
||||
if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result))
|
||||
return -EIO;
|
||||
|
||||
*value = FIELD_GET(ACER_GAMING_MISC_SETTING_VALUE_MASK, result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic Device (interface-independent)
|
||||
*/
|
||||
|
@ -1744,26 +1907,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
|
||||
*/
|
||||
|
@ -1783,8 +1926,12 @@ static int acer_toggle_turbo(void)
|
|||
WMID_gaming_set_fan_mode(0x1);
|
||||
|
||||
/* Set OC to normal */
|
||||
WMID_gaming_set_u64(0x5, ACER_CAP_TURBO_OC);
|
||||
WMID_gaming_set_u64(0x7, ACER_CAP_TURBO_OC);
|
||||
if (has_cap(ACER_CAP_TURBO_OC)) {
|
||||
WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_1,
|
||||
ACER_WMID_OC_NORMAL);
|
||||
WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_2,
|
||||
ACER_WMID_OC_NORMAL);
|
||||
}
|
||||
} else {
|
||||
/* Turn on turbo led */
|
||||
WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED);
|
||||
|
@ -1793,22 +1940,25 @@ static int acer_toggle_turbo(void)
|
|||
WMID_gaming_set_fan_mode(0x2);
|
||||
|
||||
/* Set OC to turbo mode */
|
||||
WMID_gaming_set_u64(0x205, ACER_CAP_TURBO_OC);
|
||||
WMID_gaming_set_u64(0x207, ACER_CAP_TURBO_OC);
|
||||
if (has_cap(ACER_CAP_TURBO_OC)) {
|
||||
WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_1,
|
||||
ACER_WMID_OC_TURBO);
|
||||
WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_2,
|
||||
ACER_WMID_OC_TURBO);
|
||||
}
|
||||
}
|
||||
return turbo_led_state;
|
||||
}
|
||||
|
||||
static int
|
||||
acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
acer_predator_v4_platform_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
u8 tp;
|
||||
int err;
|
||||
|
||||
err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, &tp);
|
||||
|
||||
if (err < 0)
|
||||
err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, &tp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (tp) {
|
||||
|
@ -1835,74 +1985,112 @@ acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof,
|
|||
}
|
||||
|
||||
static int
|
||||
acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
acer_predator_v4_platform_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int tp;
|
||||
acpi_status status;
|
||||
int err, tp;
|
||||
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI;
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
|
||||
break;
|
||||
case PLATFORM_PROFILE_QUIET:
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI;
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
|
||||
break;
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
status = WMI_gaming_execute_u64(
|
||||
ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL);
|
||||
err = WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
|
||||
if (tp != acer_predator_v4_max_perf)
|
||||
last_non_turbo_profile = tp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acer_platform_profile_setup(void)
|
||||
static int
|
||||
acer_predator_v4_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
unsigned long supported_profiles;
|
||||
int err;
|
||||
|
||||
err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_SUPPORTED_PROFILES,
|
||||
(u8 *)&supported_profiles);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Iterate through supported profiles in order of increasing performance */
|
||||
if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_ECO, &supported_profiles)) {
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
|
||||
last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
|
||||
}
|
||||
|
||||
if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET, &supported_profiles)) {
|
||||
set_bit(PLATFORM_PROFILE_QUIET, choices);
|
||||
acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
|
||||
last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
|
||||
}
|
||||
|
||||
if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED, &supported_profiles)) {
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
|
||||
last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
|
||||
}
|
||||
|
||||
if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE, &supported_profiles)) {
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
|
||||
acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
|
||||
|
||||
/* We only use this profile as a fallback option in case no prior
|
||||
* profile is supported.
|
||||
*/
|
||||
if (last_non_turbo_profile < 0)
|
||||
last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
|
||||
}
|
||||
|
||||
if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO, &supported_profiles)) {
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
|
||||
|
||||
/* We need to handle the hypothetical case where only the turbo profile
|
||||
* is supported. In this case the turbo toggle will essentially be a
|
||||
* no-op.
|
||||
*/
|
||||
if (last_non_turbo_profile < 0)
|
||||
last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops acer_predator_v4_platform_profile_ops = {
|
||||
.probe = acer_predator_v4_platform_profile_probe,
|
||||
.profile_get = acer_predator_v4_platform_profile_get,
|
||||
.profile_set = acer_predator_v4_platform_profile_set,
|
||||
};
|
||||
|
||||
static int acer_platform_profile_setup(struct platform_device *device)
|
||||
{
|
||||
if (quirks->predator_v4) {
|
||||
int err;
|
||||
|
||||
platform_profile_handler.profile_get =
|
||||
acer_predator_v4_platform_profile_get;
|
||||
platform_profile_handler.profile_set =
|
||||
acer_predator_v4_platform_profile_set;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE,
|
||||
platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE,
|
||||
platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED,
|
||||
platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_QUIET,
|
||||
platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER,
|
||||
platform_profile_handler.choices);
|
||||
|
||||
err = platform_profile_register(&platform_profile_handler);
|
||||
if (err)
|
||||
return err;
|
||||
platform_profile_device = devm_platform_profile_register(
|
||||
&device->dev, "acer-wmi", NULL, &acer_predator_v4_platform_profile_ops);
|
||||
if (IS_ERR(platform_profile_device))
|
||||
return PTR_ERR(platform_profile_device);
|
||||
|
||||
platform_profile_support = true;
|
||||
|
||||
/* Set default non-turbo profile */
|
||||
last_non_turbo_profile =
|
||||
ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1910,83 +2098,41 @@ static int acer_platform_profile_setup(void)
|
|||
static int acer_thermal_profile_change(void)
|
||||
{
|
||||
/*
|
||||
* This mode key can rotate each mode or toggle turbo mode.
|
||||
* On battery, only ECO and BALANCED mode are available.
|
||||
* This mode key will either cycle through each mode or toggle the
|
||||
* most performant profile.
|
||||
*/
|
||||
if (quirks->predator_v4) {
|
||||
u8 current_tp;
|
||||
int tp, err;
|
||||
u64 on_AC;
|
||||
acpi_status status;
|
||||
int err, tp;
|
||||
|
||||
err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET,
|
||||
¤t_tp);
|
||||
if (cycle_gaming_thermal_profile) {
|
||||
platform_profile_cycle();
|
||||
} else {
|
||||
/* Do nothing if no suitable platform profiles where found */
|
||||
if (last_non_turbo_profile < 0)
|
||||
return 0;
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = WMID_gaming_get_misc_setting(
|
||||
ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, ¤t_tp);
|
||||
if (err)
|
||||
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;
|
||||
|
||||
switch (current_tp) {
|
||||
case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO:
|
||||
if (!on_AC)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
else if (cycle_gaming_thermal_profile)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
|
||||
else
|
||||
if (current_tp == acer_predator_v4_max_perf)
|
||||
tp = last_non_turbo_profile;
|
||||
break;
|
||||
case ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE:
|
||||
if (!on_AC)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
else
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
|
||||
break;
|
||||
case ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED:
|
||||
if (!on_AC)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
|
||||
else if (cycle_gaming_thermal_profile)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI;
|
||||
else
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
|
||||
break;
|
||||
case ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET:
|
||||
if (!on_AC)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
else if (cycle_gaming_thermal_profile)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
else
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
|
||||
break;
|
||||
case ACER_PREDATOR_V4_THERMAL_PROFILE_ECO:
|
||||
if (!on_AC)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
|
||||
else if (cycle_gaming_thermal_profile)
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI;
|
||||
else
|
||||
tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
tp = acer_predator_v4_max_perf;
|
||||
|
||||
err = WMID_gaming_set_misc_setting(
|
||||
ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Store last profile for toggle */
|
||||
if (current_tp != acer_predator_v4_max_perf)
|
||||
last_non_turbo_profile = current_tp;
|
||||
|
||||
platform_profile_notify(platform_profile_device);
|
||||
}
|
||||
|
||||
status = WMI_gaming_execute_u64(
|
||||
ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
/* Store non-turbo profile for turbo mode toggle*/
|
||||
if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
|
||||
last_non_turbo_profile = tp;
|
||||
|
||||
platform_profile_notify();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2280,6 +2426,9 @@ static void acer_wmi_notify(union acpi_object *obj, void *context)
|
|||
if (return_value.key_num == 0x5 && has_cap(ACER_CAP_PLATFORM_PROFILE))
|
||||
acer_thermal_profile_change();
|
||||
break;
|
||||
case WMID_AC_EVENT:
|
||||
/* We ignore AC events here */
|
||||
break;
|
||||
default:
|
||||
pr_warn("Unknown function number - %d - %d\n",
|
||||
return_value.function, return_value.key_num);
|
||||
|
@ -2530,12 +2679,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;
|
||||
|
@ -2544,8 +2693,6 @@ static int acer_platform_probe(struct platform_device *device)
|
|||
return 0;
|
||||
|
||||
error_hwmon:
|
||||
if (platform_profile_support)
|
||||
platform_profile_remove();
|
||||
error_platform_profile:
|
||||
acer_rfkill_exit();
|
||||
error_rfkill:
|
||||
|
@ -2566,9 +2713,6 @@ static void acer_platform_remove(struct platform_device *device)
|
|||
acer_backlight_exit();
|
||||
|
||||
acer_rfkill_exit();
|
||||
|
||||
if (platform_profile_support)
|
||||
platform_profile_remove();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -2655,43 +2799,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 +2895,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;
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
@ -41,24 +42,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
|
||||
|
@ -98,7 +84,6 @@
|
|||
|
||||
#define DELAY_MIN_US 2000
|
||||
#define DELAY_MAX_US 3000
|
||||
#define FIFO_SIZE 4096
|
||||
|
||||
enum amd_pmc_def {
|
||||
MSG_TEST = 0x01,
|
||||
|
@ -106,24 +91,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)},
|
||||
|
@ -147,25 +147,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)
|
||||
{
|
||||
|
@ -194,155 +182,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) {
|
||||
|
@ -351,18 +190,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;
|
||||
}
|
||||
|
@ -530,8 +374,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]);
|
||||
}
|
||||
|
||||
|
@ -626,20 +470,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);
|
||||
|
@ -649,14 +479,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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -664,10 +497,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;
|
||||
|
@ -675,26 +508,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);
|
||||
guard(mutex)(&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;
|
||||
|
@ -707,7 +540,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
|
|||
PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
|
||||
if (rc) {
|
||||
dev_err(dev->dev, "failed to talk to SMU\n");
|
||||
goto out_unlock;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write zero to response register */
|
||||
|
@ -725,7 +558,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
|
|||
PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
|
||||
if (rc) {
|
||||
dev_err(dev->dev, "SMU response timed out\n");
|
||||
goto out_unlock;
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
|
@ -739,21 +572,19 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
|
|||
case AMD_PMC_RESULT_CMD_REJECT_BUSY:
|
||||
dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
|
||||
rc = -EBUSY;
|
||||
goto out_unlock;
|
||||
break;
|
||||
case AMD_PMC_RESULT_CMD_UNKNOWN:
|
||||
dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
|
||||
rc = -EINVAL;
|
||||
goto out_unlock;
|
||||
break;
|
||||
case AMD_PMC_RESULT_CMD_REJECT_PREREQ:
|
||||
case AMD_PMC_RESULT_FAILED:
|
||||
default:
|
||||
dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
|
||||
rc = -EIO;
|
||||
goto out_unlock;
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
amd_pmc_dump_registers(dev);
|
||||
return rc;
|
||||
}
|
||||
|
@ -882,7 +713,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);
|
||||
}
|
||||
|
@ -901,7 +732,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);
|
||||
}
|
||||
|
@ -928,7 +759,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);
|
||||
|
||||
|
@ -982,74 +813,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;
|
||||
|
@ -1107,12 +870,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);
|
||||
|
@ -1123,6 +880,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,17 +321,29 @@ 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;
|
||||
struct apmf_sbios_req req;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pmf_dev->update_mutex);
|
||||
guard(mutex)(&pmf_dev->update_mutex);
|
||||
ret = apmf_get_sbios_requests(pmf_dev, &req);
|
||||
if (ret) {
|
||||
dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
|
||||
|
@ -353,8 +365,6 @@ static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
|
|||
if (pmf_dev->amt_enabled)
|
||||
amd_pmf_update_2_cql(pmf_dev, req.cql_event);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&pmf_dev->update_mutex);
|
||||
}
|
||||
|
||||
static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
|
||||
|
@ -430,6 +440,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 +499,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)
|
||||
|
|
|
@ -127,7 +127,8 @@ static void amd_pmf_get_metrics(struct work_struct *work)
|
|||
ktime_t time_elapsed_ms;
|
||||
int socket_power;
|
||||
|
||||
mutex_lock(&dev->update_mutex);
|
||||
guard(mutex)(&dev->update_mutex);
|
||||
|
||||
/* Transfer table contents */
|
||||
memset(dev->buf, 0, sizeof(dev->m_table));
|
||||
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
|
||||
|
@ -149,7 +150,6 @@ static void amd_pmf_get_metrics(struct work_struct *work)
|
|||
|
||||
dev->start_time = ktime_to_ms(ktime_get());
|
||||
schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
|
||||
mutex_unlock(&dev->update_mutex);
|
||||
}
|
||||
|
||||
static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
|
||||
|
@ -181,7 +181,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
|
|||
int rc;
|
||||
u32 val;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
guard(mutex)(&dev->lock);
|
||||
|
||||
/* Wait until we get a valid response */
|
||||
rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
|
||||
|
@ -189,7 +189,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
|
|||
PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
|
||||
if (rc) {
|
||||
dev_err(dev->dev, "failed to talk to SMU\n");
|
||||
goto out_unlock;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write zero to response register */
|
||||
|
@ -207,7 +207,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
|
|||
PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
|
||||
if (rc) {
|
||||
dev_err(dev->dev, "SMU response timed out\n");
|
||||
goto out_unlock;
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
|
@ -221,21 +221,19 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
|
|||
case AMD_PMF_RESULT_CMD_REJECT_BUSY:
|
||||
dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
|
||||
rc = -EBUSY;
|
||||
goto out_unlock;
|
||||
break;
|
||||
case AMD_PMF_RESULT_CMD_UNKNOWN:
|
||||
dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
|
||||
rc = -EINVAL;
|
||||
goto out_unlock;
|
||||
break;
|
||||
case AMD_PMF_RESULT_CMD_REJECT_PREREQ:
|
||||
case AMD_PMF_RESULT_FAILED:
|
||||
default:
|
||||
dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
|
||||
rc = -EIO;
|
||||
goto out_unlock;
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
amd_pmf_dump_registers(dev);
|
||||
return rc;
|
||||
}
|
||||
|
@ -373,7 +371,6 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
|
|||
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) ||
|
||||
is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) {
|
||||
power_supply_unreg_notifier(&dev->pwr_src_notifier);
|
||||
amd_pmf_deinit_sps(dev);
|
||||
}
|
||||
|
||||
if (dev->smart_pc_enabled) {
|
||||
|
@ -456,7 +453,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);
|
||||
}
|
||||
}
|
|
@ -338,7 +338,7 @@ struct amd_pmf_dev {
|
|||
struct mutex lock; /* protects the PMF interface */
|
||||
u32 supported_func;
|
||||
enum platform_profile_option current_profile;
|
||||
struct platform_profile_handler pprof;
|
||||
struct device *ppdev; /* platform profile class device */
|
||||
struct dentry *dbgfs_dir;
|
||||
int hb_interval; /* SBIOS heartbeat interval */
|
||||
struct delayed_work heart_beat;
|
||||
|
@ -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,30 @@ 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,
|
||||
};
|
||||
|
||||
enum platform_type {
|
||||
PTYPE_UNKNOWN = 0,
|
||||
LID_CLOSE,
|
||||
CLAMSHELL,
|
||||
FLAT,
|
||||
TENT,
|
||||
STAND,
|
||||
TABLET,
|
||||
BOOK,
|
||||
PRESENTATION,
|
||||
PULL_FWD,
|
||||
PTYPE_INVALID = 0xf,
|
||||
};
|
||||
|
||||
/* Command ids for TA communication */
|
||||
enum ta_pmf_command {
|
||||
TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE,
|
||||
|
@ -657,7 +683,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;
|
||||
|
@ -667,7 +694,9 @@ struct ta_pmf_condition_info {
|
|||
u32 device_state;
|
||||
u32 socket_power;
|
||||
u32 skin_temperature;
|
||||
u32 rsvd3[5];
|
||||
u32 rsvd3[2];
|
||||
u32 platform_type;
|
||||
u32 rsvd3_1[2];
|
||||
u32 ambient_light;
|
||||
u32 length;
|
||||
u32 avg_c0residency;
|
||||
|
@ -751,7 +780,6 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
|
|||
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
|
||||
struct amd_pmf_static_slider_granular *table);
|
||||
int amd_pmf_init_sps(struct amd_pmf_dev *dev);
|
||||
void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
|
||||
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
|
||||
struct apmf_static_slider_granular_output *output);
|
||||
bool is_pprof_balanced(struct amd_pmf_dev *pmf);
|
||||
|
@ -797,7 +825,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 */
|
||||
|
|
|
@ -16,6 +16,46 @@
|
|||
#include "pmf.h"
|
||||
|
||||
#ifdef CONFIG_AMD_PMF_DEBUG
|
||||
static const char *platform_type_as_str(u16 platform_type)
|
||||
{
|
||||
switch (platform_type) {
|
||||
case CLAMSHELL:
|
||||
return "CLAMSHELL";
|
||||
case FLAT:
|
||||
return "FLAT";
|
||||
case TENT:
|
||||
return "TENT";
|
||||
case STAND:
|
||||
return "STAND";
|
||||
case TABLET:
|
||||
return "TABLET";
|
||||
case BOOK:
|
||||
return "BOOK";
|
||||
case PRESENTATION:
|
||||
return "PRESENTATION";
|
||||
case PULL_FWD:
|
||||
return "PULL_FWD";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *laptop_placement_as_str(u16 device_state)
|
||||
{
|
||||
switch (device_state) {
|
||||
case ON_TABLE:
|
||||
return "ON_TABLE";
|
||||
case ON_LAP_MOTION:
|
||||
return "ON_LAP_MOTION";
|
||||
case IN_BAG:
|
||||
return "IN_BAG";
|
||||
case OUT_OF_BAG:
|
||||
return "OUT_OF_BAG";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *ta_slider_as_str(unsigned int state)
|
||||
{
|
||||
switch (state) {
|
||||
|
@ -47,12 +87,38 @@ 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, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type));
|
||||
dev_dbg(dev->dev, "Laptop placement: %s\n",
|
||||
laptop_placement_as_str(in->ev_info.device_state));
|
||||
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;
|
||||
|
@ -190,6 +256,14 @@ static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact
|
|||
} else {
|
||||
dev_dbg(dev->dev, "HPD is not enabled/detected\n");
|
||||
}
|
||||
|
||||
/* Get SRA (Secondary Accelerometer) data */
|
||||
if (!amd_get_sfh_info(&sfh_info, MT_SRA)) {
|
||||
in->ev_info.platform_type = sfh_info.platform_type;
|
||||
in->ev_info.device_state = sfh_info.laptop_placement;
|
||||
} else {
|
||||
dev_dbg(dev->dev, "SRA is not enabled/detected\n");
|
||||
}
|
||||
}
|
||||
|
||||
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
|
||||
|
@ -201,4 +275,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);
|
||||
}
|
||||
|
|
|
@ -282,10 +282,10 @@ bool is_pprof_balanced(struct amd_pmf_dev *pmf)
|
|||
return (pmf->current_profile == PLATFORM_PROFILE_BALANCED) ? true : false;
|
||||
}
|
||||
|
||||
static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
|
||||
static int amd_pmf_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
|
||||
struct amd_pmf_dev *pmf = dev_get_drvdata(dev);
|
||||
|
||||
*profile = pmf->current_profile;
|
||||
return 0;
|
||||
|
@ -363,10 +363,10 @@ int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
|
||||
static int amd_pmf_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
|
||||
struct amd_pmf_dev *pmf = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
pmf->current_profile = profile;
|
||||
|
@ -387,10 +387,23 @@ static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops amd_pmf_profile_ops = {
|
||||
.probe = amd_pmf_profile_probe,
|
||||
.profile_get = amd_pmf_profile_get,
|
||||
.profile_set = amd_pmf_profile_set,
|
||||
};
|
||||
|
||||
int amd_pmf_init_sps(struct amd_pmf_dev *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
dev->current_profile = PLATFORM_PROFILE_BALANCED;
|
||||
|
||||
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
|
||||
|
@ -405,24 +418,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
|
|||
amd_pmf_set_sps_power_limits(dev);
|
||||
}
|
||||
|
||||
dev->pprof.profile_get = amd_pmf_profile_get;
|
||||
dev->pprof.profile_set = amd_pmf_profile_set;
|
||||
|
||||
/* Setup supported modes */
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices);
|
||||
|
||||
/* Create platform_profile structure and register */
|
||||
err = platform_profile_register(&dev->pprof);
|
||||
if (err)
|
||||
dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
|
||||
err);
|
||||
dev->ppdev = devm_platform_profile_register(dev->dev, "amd-pmf", dev,
|
||||
&amd_pmf_profile_ops);
|
||||
if (IS_ERR(dev->ppdev))
|
||||
dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %ld\n",
|
||||
PTR_ERR(dev->ppdev));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
|
||||
{
|
||||
platform_profile_remove();
|
||||
return PTR_ERR_OR_ZERO(dev->ppdev);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ MODULE_PARM_DESC(tablet_mode_sw, "Tablet mode detect: -1:auto 0:disable 1:kbd-do
|
|||
static struct quirk_entry *quirks;
|
||||
static bool atkbd_reports_vol_keys;
|
||||
|
||||
static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port)
|
||||
static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
|
||||
void *context)
|
||||
{
|
||||
static bool extended_e0;
|
||||
static bool extended_e1;
|
||||
|
|
|
@ -313,7 +313,7 @@ struct asus_wmi {
|
|||
bool mid_fan_curve_available;
|
||||
struct fan_curve_data custom_fan_curves[3];
|
||||
|
||||
struct platform_profile_handler platform_profile_handler;
|
||||
struct device *ppdev;
|
||||
bool platform_profile_support;
|
||||
|
||||
// The RSOC controls the maximum charging percentage.
|
||||
|
@ -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->ppdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -3793,13 +3793,13 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
|
|||
static DEVICE_ATTR_RW(throttle_thermal_policy);
|
||||
|
||||
/* Platform profile ***********************************************************/
|
||||
static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
static int asus_wmi_platform_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
int tp;
|
||||
|
||||
asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
|
||||
asus = dev_get_drvdata(dev);
|
||||
tp = asus->throttle_thermal_policy_mode;
|
||||
|
||||
switch (tp) {
|
||||
|
@ -3819,13 +3819,13 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
static int asus_wmi_platform_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
int tp;
|
||||
|
||||
asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
|
||||
asus = dev_get_drvdata(dev);
|
||||
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
|
@ -3845,6 +3845,21 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
|||
return throttle_thermal_policy_write(asus);
|
||||
}
|
||||
|
||||
static int asus_wmi_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_QUIET, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops asus_wmi_platform_profile_ops = {
|
||||
.probe = asus_wmi_platform_profile_probe,
|
||||
.profile_get = asus_wmi_platform_profile_get,
|
||||
.profile_set = asus_wmi_platform_profile_set,
|
||||
};
|
||||
|
||||
static int platform_profile_setup(struct asus_wmi *asus)
|
||||
{
|
||||
struct device *dev = &asus->platform_device->dev;
|
||||
|
@ -3869,22 +3884,11 @@ 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.profile_get = asus_wmi_platform_profile_get;
|
||||
asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_QUIET, asus->platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED,
|
||||
asus->platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE,
|
||||
asus->platform_profile_handler.choices);
|
||||
|
||||
err = platform_profile_register(&asus->platform_profile_handler);
|
||||
if (err == -EEXIST) {
|
||||
pr_warn("%s, a platform_profile handler is already registered\n", __func__);
|
||||
return 0;
|
||||
} else if (err) {
|
||||
pr_err("%s, failed at platform_profile_register: %d\n", __func__, err);
|
||||
return err;
|
||||
asus->ppdev = devm_platform_profile_register(dev, "asus-wmi", asus,
|
||||
&asus_wmi_platform_profile_ops);
|
||||
if (IS_ERR(asus->ppdev)) {
|
||||
dev_err(dev, "Failed to register a platform_profile class device\n");
|
||||
return PTR_ERR(asus->ppdev);
|
||||
}
|
||||
|
||||
asus->platform_profile_support = true;
|
||||
|
@ -4815,7 +4819,7 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (asus->driver->i8042_filter) {
|
||||
err = i8042_install_filter(asus->driver->i8042_filter);
|
||||
err = i8042_install_filter(asus->driver->i8042_filter, NULL);
|
||||
if (err)
|
||||
pr_warn("Unable to install key filter - %d\n", err);
|
||||
}
|
||||
|
@ -4842,8 +4846,6 @@ fail_input:
|
|||
fail_sysfs:
|
||||
fail_custom_fan_curve:
|
||||
fail_platform_profile_setup:
|
||||
if (asus->platform_profile_support)
|
||||
platform_profile_remove();
|
||||
fail_fan_boost_mode:
|
||||
fail_platform:
|
||||
kfree(asus);
|
||||
|
@ -4869,9 +4871,6 @@ static void asus_wmi_remove(struct platform_device *device)
|
|||
throttle_thermal_policy_set_default(asus);
|
||||
asus_wmi_battery_exit(asus);
|
||||
|
||||
if (asus->platform_profile_support)
|
||||
platform_profile_remove();
|
||||
|
||||
kfree(asus);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,8 +73,7 @@ struct asus_wmi_driver {
|
|||
void (*key_filter) (struct asus_wmi_driver *driver, int *code,
|
||||
unsigned int *value, bool *autorelease);
|
||||
/* Optional standard i8042 filter */
|
||||
bool (*i8042_filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio);
|
||||
i8042_filter_t i8042_filter;
|
||||
|
||||
int (*probe) (struct platform_device *device);
|
||||
void (*detect_quirks) (struct asus_wmi_driver *driver);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -385,12 +385,6 @@ struct color_platform {
|
|||
u8 red;
|
||||
} __packed;
|
||||
|
||||
struct platform_zone {
|
||||
u8 location;
|
||||
struct device_attribute *attr;
|
||||
struct color_platform colors;
|
||||
};
|
||||
|
||||
struct wmax_brightness_args {
|
||||
u32 led_mask;
|
||||
u32 percentage;
|
||||
|
@ -420,22 +414,9 @@ struct wmax_u32_args {
|
|||
};
|
||||
|
||||
static struct platform_device *platform_device;
|
||||
static struct device_attribute *zone_dev_attrs;
|
||||
static struct attribute **zone_attrs;
|
||||
static struct platform_zone *zone_data;
|
||||
static struct platform_profile_handler pp_handler;
|
||||
static struct color_platform colors[4];
|
||||
static enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
|
||||
|
||||
static struct platform_driver platform_driver = {
|
||||
.driver = {
|
||||
.name = "alienware-wmi",
|
||||
}
|
||||
};
|
||||
|
||||
static struct attribute_group zone_attribute_group = {
|
||||
.name = "rgb_zones",
|
||||
};
|
||||
|
||||
static u8 interface;
|
||||
static u8 lighting_control_state;
|
||||
static u8 global_brightness;
|
||||
|
@ -443,7 +424,7 @@ static u8 global_brightness;
|
|||
/*
|
||||
* Helpers used for zone control
|
||||
*/
|
||||
static int parse_rgb(const char *buf, struct platform_zone *zone)
|
||||
static int parse_rgb(const char *buf, struct color_platform *colors)
|
||||
{
|
||||
long unsigned int rgb;
|
||||
int ret;
|
||||
|
@ -463,28 +444,14 @@ static int parse_rgb(const char *buf, struct platform_zone *zone)
|
|||
repackager.package = rgb & 0x0f0f0f0f;
|
||||
pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
|
||||
repackager.cp.red, repackager.cp.green, repackager.cp.blue);
|
||||
zone->colors = repackager.cp;
|
||||
*colors = repackager.cp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_zone *match_zone(struct device_attribute *attr)
|
||||
{
|
||||
u8 zone;
|
||||
|
||||
for (zone = 0; zone < quirks->num_zones; zone++) {
|
||||
if ((struct device_attribute *)zone_data[zone].attr == attr) {
|
||||
pr_debug("alienware-wmi: matched zone location: %d\n",
|
||||
zone_data[zone].location);
|
||||
return &zone_data[zone];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Individual RGB zone control
|
||||
*/
|
||||
static int alienware_update_led(struct platform_zone *zone)
|
||||
static int alienware_update_led(u8 location)
|
||||
{
|
||||
int method_id;
|
||||
acpi_status status;
|
||||
|
@ -493,8 +460,8 @@ static int alienware_update_led(struct platform_zone *zone)
|
|||
struct legacy_led_args legacy_args;
|
||||
struct wmax_led_args wmax_basic_args;
|
||||
if (interface == WMAX) {
|
||||
wmax_basic_args.led_mask = 1 << zone->location;
|
||||
wmax_basic_args.colors = zone->colors;
|
||||
wmax_basic_args.led_mask = 1 << location;
|
||||
wmax_basic_args.colors = colors[location];
|
||||
wmax_basic_args.state = lighting_control_state;
|
||||
guid = WMAX_CONTROL_GUID;
|
||||
method_id = WMAX_METHOD_ZONE_CONTROL;
|
||||
|
@ -502,7 +469,7 @@ static int alienware_update_led(struct platform_zone *zone)
|
|||
input.length = sizeof(wmax_basic_args);
|
||||
input.pointer = &wmax_basic_args;
|
||||
} else {
|
||||
legacy_args.colors = zone->colors;
|
||||
legacy_args.colors = colors[location];
|
||||
legacy_args.brightness = global_brightness;
|
||||
legacy_args.state = 0;
|
||||
if (lighting_control_state == LEGACY_BOOTING ||
|
||||
|
@ -511,7 +478,7 @@ static int alienware_update_led(struct platform_zone *zone)
|
|||
legacy_args.state = lighting_control_state;
|
||||
} else
|
||||
guid = LEGACY_CONTROL_GUID;
|
||||
method_id = zone->location + 1;
|
||||
method_id = location + 1;
|
||||
|
||||
input.length = sizeof(legacy_args);
|
||||
input.pointer = &legacy_args;
|
||||
|
@ -525,35 +492,153 @@ static int alienware_update_led(struct platform_zone *zone)
|
|||
}
|
||||
|
||||
static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
char *buf, u8 location)
|
||||
{
|
||||
struct platform_zone *target_zone;
|
||||
target_zone = match_zone(attr);
|
||||
if (target_zone == NULL)
|
||||
return sprintf(buf, "red: -1, green: -1, blue: -1\n");
|
||||
return sprintf(buf, "red: %d, green: %d, blue: %d\n",
|
||||
target_zone->colors.red,
|
||||
target_zone->colors.green, target_zone->colors.blue);
|
||||
colors[location].red, colors[location].green,
|
||||
colors[location].blue);
|
||||
|
||||
}
|
||||
|
||||
static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t zone_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count, u8 location)
|
||||
{
|
||||
struct platform_zone *target_zone;
|
||||
int ret;
|
||||
target_zone = match_zone(attr);
|
||||
if (target_zone == NULL) {
|
||||
pr_err("alienware-wmi: invalid target zone\n");
|
||||
return 1;
|
||||
}
|
||||
ret = parse_rgb(buf, target_zone);
|
||||
|
||||
ret = parse_rgb(buf, &colors[location]);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = alienware_update_led(target_zone);
|
||||
|
||||
ret = alienware_update_led(location);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static ssize_t zone00_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return zone_show(dev, attr, buf, 0);
|
||||
}
|
||||
|
||||
static ssize_t zone00_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return zone_store(dev, attr, buf, count, 0);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(zone00);
|
||||
|
||||
static ssize_t zone01_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return zone_show(dev, attr, buf, 1);
|
||||
}
|
||||
|
||||
static ssize_t zone01_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return zone_store(dev, attr, buf, count, 1);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(zone01);
|
||||
|
||||
static ssize_t zone02_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return zone_show(dev, attr, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t zone02_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return zone_store(dev, attr, buf, count, 2);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(zone02);
|
||||
|
||||
static ssize_t zone03_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return zone_show(dev, attr, buf, 3);
|
||||
}
|
||||
|
||||
static ssize_t zone03_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return zone_store(dev, attr, buf, count, 3);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(zone03);
|
||||
|
||||
/*
|
||||
* Lighting control state device attribute (Global)
|
||||
*/
|
||||
static ssize_t lighting_control_state_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
if (lighting_control_state == LEGACY_BOOTING)
|
||||
return sysfs_emit(buf, "[booting] running suspend\n");
|
||||
else if (lighting_control_state == LEGACY_SUSPEND)
|
||||
return sysfs_emit(buf, "booting running [suspend]\n");
|
||||
|
||||
return sysfs_emit(buf, "booting [running] suspend\n");
|
||||
}
|
||||
|
||||
static ssize_t lighting_control_state_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (strcmp(buf, "booting\n") == 0)
|
||||
val = LEGACY_BOOTING;
|
||||
else if (strcmp(buf, "suspend\n") == 0)
|
||||
val = LEGACY_SUSPEND;
|
||||
else if (interface == LEGACY)
|
||||
val = LEGACY_RUNNING;
|
||||
else
|
||||
val = WMAX_RUNNING;
|
||||
|
||||
lighting_control_state = val;
|
||||
pr_debug("alienware-wmi: updated control state to %d\n",
|
||||
lighting_control_state);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(lighting_control_state);
|
||||
|
||||
static umode_t zone_attr_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
if (n < quirks->num_zones + 1)
|
||||
return attr->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool zone_group_visible(struct kobject *kobj)
|
||||
{
|
||||
return quirks->num_zones > 0;
|
||||
}
|
||||
DEFINE_SYSFS_GROUP_VISIBLE(zone);
|
||||
|
||||
static struct attribute *zone_attrs[] = {
|
||||
&dev_attr_lighting_control_state.attr,
|
||||
&dev_attr_zone00.attr,
|
||||
&dev_attr_zone01.attr,
|
||||
&dev_attr_zone02.attr,
|
||||
&dev_attr_zone03.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group zone_attribute_group = {
|
||||
.name = "rgb_zones",
|
||||
.is_visible = SYSFS_GROUP_VISIBLE(zone),
|
||||
.attrs = zone_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* LED Brightness (Global)
|
||||
*/
|
||||
|
@ -582,7 +667,7 @@ static void global_led_set(struct led_classdev *led_cdev,
|
|||
if (interface == WMAX)
|
||||
ret = wmax_brightness(brightness);
|
||||
else
|
||||
ret = alienware_update_led(&zone_data[0]);
|
||||
ret = alienware_update_led(0);
|
||||
if (ret)
|
||||
pr_err("LED brightness update failed\n");
|
||||
}
|
||||
|
@ -598,46 +683,8 @@ static struct led_classdev global_led = {
|
|||
.name = "alienware::global_brightness",
|
||||
};
|
||||
|
||||
/*
|
||||
* Lighting control state device attribute (Global)
|
||||
*/
|
||||
static ssize_t show_control_state(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (lighting_control_state == LEGACY_BOOTING)
|
||||
return sysfs_emit(buf, "[booting] running suspend\n");
|
||||
else if (lighting_control_state == LEGACY_SUSPEND)
|
||||
return sysfs_emit(buf, "booting running [suspend]\n");
|
||||
return sysfs_emit(buf, "booting [running] suspend\n");
|
||||
}
|
||||
|
||||
static ssize_t store_control_state(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
long unsigned int val;
|
||||
if (strcmp(buf, "booting\n") == 0)
|
||||
val = LEGACY_BOOTING;
|
||||
else if (strcmp(buf, "suspend\n") == 0)
|
||||
val = LEGACY_SUSPEND;
|
||||
else if (interface == LEGACY)
|
||||
val = LEGACY_RUNNING;
|
||||
else
|
||||
val = WMAX_RUNNING;
|
||||
lighting_control_state = val;
|
||||
pr_debug("alienware-wmi: updated control state to %d\n",
|
||||
lighting_control_state);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
|
||||
store_control_state);
|
||||
|
||||
static int alienware_zone_init(struct platform_device *dev)
|
||||
{
|
||||
u8 zone;
|
||||
char *name;
|
||||
|
||||
if (interface == WMAX) {
|
||||
lighting_control_state = WMAX_RUNNING;
|
||||
} else if (interface == LEGACY) {
|
||||
|
@ -646,68 +693,15 @@ static int alienware_zone_init(struct platform_device *dev)
|
|||
global_led.max_brightness = 0x0F;
|
||||
global_brightness = global_led.max_brightness;
|
||||
|
||||
/*
|
||||
* - zone_dev_attrs num_zones + 1 is for individual zones and then
|
||||
* null terminated
|
||||
* - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs +
|
||||
* the lighting control + null terminated
|
||||
* - zone_data num_zones is for the distinct zones
|
||||
*/
|
||||
zone_dev_attrs =
|
||||
kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute),
|
||||
GFP_KERNEL);
|
||||
if (!zone_dev_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
zone_attrs =
|
||||
kcalloc(quirks->num_zones + 2, sizeof(struct attribute *),
|
||||
GFP_KERNEL);
|
||||
if (!zone_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
zone_data =
|
||||
kcalloc(quirks->num_zones, sizeof(struct platform_zone),
|
||||
GFP_KERNEL);
|
||||
if (!zone_data)
|
||||
return -ENOMEM;
|
||||
|
||||
for (zone = 0; zone < quirks->num_zones; zone++) {
|
||||
name = kasprintf(GFP_KERNEL, "zone%02hhX", zone);
|
||||
if (name == NULL)
|
||||
return 1;
|
||||
sysfs_attr_init(&zone_dev_attrs[zone].attr);
|
||||
zone_dev_attrs[zone].attr.name = name;
|
||||
zone_dev_attrs[zone].attr.mode = 0644;
|
||||
zone_dev_attrs[zone].show = zone_show;
|
||||
zone_dev_attrs[zone].store = zone_set;
|
||||
zone_data[zone].location = zone;
|
||||
zone_attrs[zone] = &zone_dev_attrs[zone].attr;
|
||||
zone_data[zone].attr = &zone_dev_attrs[zone];
|
||||
}
|
||||
zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
|
||||
zone_attribute_group.attrs = zone_attrs;
|
||||
|
||||
led_classdev_register(&dev->dev, &global_led);
|
||||
|
||||
return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
|
||||
return led_classdev_register(&dev->dev, &global_led);
|
||||
}
|
||||
|
||||
static void alienware_zone_exit(struct platform_device *dev)
|
||||
{
|
||||
u8 zone;
|
||||
|
||||
if (!quirks->num_zones)
|
||||
return;
|
||||
|
||||
sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
|
||||
led_classdev_unregister(&global_led);
|
||||
if (zone_dev_attrs) {
|
||||
for (zone = 0; zone < quirks->num_zones; zone++)
|
||||
kfree(zone_dev_attrs[zone].attr.name);
|
||||
}
|
||||
kfree(zone_dev_attrs);
|
||||
kfree(zone_data);
|
||||
kfree(zone_attrs);
|
||||
}
|
||||
|
||||
static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
|
||||
|
@ -742,14 +736,15 @@ static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
|
|||
* The HDMI mux sysfs node indicates the status of the HDMI input mux.
|
||||
* It can toggle between standard system GPU output and HDMI input.
|
||||
*/
|
||||
static ssize_t show_hdmi_cable(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t cable_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
|
||||
status =
|
||||
alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
WMAX_METHOD_HDMI_CABLE, &out_data);
|
||||
|
@ -763,14 +758,15 @@ static ssize_t show_hdmi_cable(struct device *dev,
|
|||
return sysfs_emit(buf, "unconnected connected [unknown]\n");
|
||||
}
|
||||
|
||||
static ssize_t show_hdmi_source(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t source_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
|
||||
status =
|
||||
alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
WMAX_METHOD_HDMI_STATUS, &out_data);
|
||||
|
@ -785,12 +781,12 @@ static ssize_t show_hdmi_source(struct device *dev,
|
|||
return sysfs_emit(buf, "input gpu [unknown]\n");
|
||||
}
|
||||
|
||||
static ssize_t toggle_hdmi_source(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t source_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
acpi_status status;
|
||||
struct wmax_basic_args args;
|
||||
acpi_status status;
|
||||
|
||||
if (strcmp(buf, "gpu\n") == 0)
|
||||
args.arg = 1;
|
||||
else if (strcmp(buf, "input\n") == 0)
|
||||
|
@ -808,9 +804,14 @@ static ssize_t toggle_hdmi_source(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
|
||||
static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
|
||||
toggle_hdmi_source);
|
||||
static DEVICE_ATTR_RO(cable);
|
||||
static DEVICE_ATTR_RW(source);
|
||||
|
||||
static bool hdmi_group_visible(struct kobject *kobj)
|
||||
{
|
||||
return quirks->hdmi_mux;
|
||||
}
|
||||
DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
|
||||
|
||||
static struct attribute *hdmi_attrs[] = {
|
||||
&dev_attr_cable.attr,
|
||||
|
@ -820,38 +821,24 @@ static struct attribute *hdmi_attrs[] = {
|
|||
|
||||
static const struct attribute_group hdmi_attribute_group = {
|
||||
.name = "hdmi",
|
||||
.is_visible = SYSFS_GROUP_VISIBLE(hdmi),
|
||||
.attrs = hdmi_attrs,
|
||||
};
|
||||
|
||||
static void remove_hdmi(struct platform_device *dev)
|
||||
{
|
||||
if (quirks->hdmi_mux > 0)
|
||||
sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group);
|
||||
}
|
||||
|
||||
static int create_hdmi(struct platform_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
|
||||
if (ret)
|
||||
remove_hdmi(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alienware GFX amplifier support
|
||||
* - Currently supports reading cable status
|
||||
* - Leaving expansion room to possibly support dock/undock events later
|
||||
*/
|
||||
static ssize_t show_amplifier_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t status_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
|
||||
status =
|
||||
alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
|
||||
|
@ -865,7 +852,13 @@ static ssize_t show_amplifier_status(struct device *dev,
|
|||
return sysfs_emit(buf, "unconnected connected [unknown]\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
|
||||
static DEVICE_ATTR_RO(status);
|
||||
|
||||
static bool amplifier_group_visible(struct kobject *kobj)
|
||||
{
|
||||
return quirks->amplifier;
|
||||
}
|
||||
DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
|
||||
|
||||
static struct attribute *amplifier_attrs[] = {
|
||||
&dev_attr_status.attr,
|
||||
|
@ -874,37 +867,23 @@ static struct attribute *amplifier_attrs[] = {
|
|||
|
||||
static const struct attribute_group amplifier_attribute_group = {
|
||||
.name = "amplifier",
|
||||
.is_visible = SYSFS_GROUP_VISIBLE(amplifier),
|
||||
.attrs = amplifier_attrs,
|
||||
};
|
||||
|
||||
static void remove_amplifier(struct platform_device *dev)
|
||||
{
|
||||
if (quirks->amplifier > 0)
|
||||
sysfs_remove_group(&dev->dev.kobj, &lifier_attribute_group);
|
||||
}
|
||||
|
||||
static int create_amplifier(struct platform_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &lifier_attribute_group);
|
||||
if (ret)
|
||||
remove_amplifier(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deep Sleep Control support
|
||||
* - Modifies BIOS setting for deep sleep control allowing extra wakeup events
|
||||
*/
|
||||
static ssize_t show_deepsleep_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
|
||||
status = alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
|
@ -919,12 +898,11 @@ static ssize_t show_deepsleep_status(struct device *dev,
|
|||
return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
|
||||
}
|
||||
|
||||
static ssize_t toggle_deepsleep(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
acpi_status status;
|
||||
struct wmax_basic_args args;
|
||||
acpi_status status;
|
||||
|
||||
if (strcmp(buf, "disabled\n") == 0)
|
||||
args.arg = 0;
|
||||
|
@ -943,7 +921,13 @@ static ssize_t toggle_deepsleep(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
|
||||
static DEVICE_ATTR_RW(deepsleep);
|
||||
|
||||
static bool deepsleep_group_visible(struct kobject *kobj)
|
||||
{
|
||||
return quirks->deepslp;
|
||||
}
|
||||
DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
|
||||
|
||||
static struct attribute *deepsleep_attrs[] = {
|
||||
&dev_attr_deepsleep.attr,
|
||||
|
@ -952,25 +936,10 @@ static struct attribute *deepsleep_attrs[] = {
|
|||
|
||||
static const struct attribute_group deepsleep_attribute_group = {
|
||||
.name = "deepsleep",
|
||||
.is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
|
||||
.attrs = deepsleep_attrs,
|
||||
};
|
||||
|
||||
static void remove_deepsleep(struct platform_device *dev)
|
||||
{
|
||||
if (quirks->deepslp > 0)
|
||||
sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
|
||||
}
|
||||
|
||||
static int create_deepsleep(struct platform_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
|
||||
if (ret)
|
||||
remove_deepsleep(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Thermal Profile control
|
||||
* - Provides thermal profile control through the Platform Profile API
|
||||
|
@ -1000,13 +969,13 @@ static bool is_wmax_thermal_code(u32 code)
|
|||
|
||||
static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
|
||||
{
|
||||
acpi_status status;
|
||||
struct wmax_u32_args in_args = {
|
||||
.operation = operation,
|
||||
.arg1 = arg,
|
||||
.arg2 = 0,
|
||||
.arg3 = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
|
||||
status = alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
WMAX_METHOD_THERMAL_INFORMATION,
|
||||
|
@ -1023,13 +992,13 @@ static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
|
|||
|
||||
static int wmax_thermal_control(u8 profile)
|
||||
{
|
||||
acpi_status status;
|
||||
struct wmax_u32_args in_args = {
|
||||
.operation = WMAX_OPERATION_ACTIVATE_PROFILE,
|
||||
.arg1 = profile,
|
||||
.arg2 = 0,
|
||||
.arg3 = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
|
||||
status = alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
|
@ -1047,13 +1016,13 @@ static int wmax_thermal_control(u8 profile)
|
|||
|
||||
static int wmax_game_shift_status(u8 operation, u32 *out_data)
|
||||
{
|
||||
acpi_status status;
|
||||
struct wmax_u32_args in_args = {
|
||||
.operation = operation,
|
||||
.arg1 = 0,
|
||||
.arg2 = 0,
|
||||
.arg3 = 0,
|
||||
};
|
||||
acpi_status status;
|
||||
|
||||
status = alienware_wmax_command(&in_args, sizeof(in_args),
|
||||
WMAX_METHOD_GAME_SHIFT_STATUS,
|
||||
|
@ -1068,7 +1037,7 @@ static int wmax_game_shift_status(u8 operation, u32 *out_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_profile_get(struct platform_profile_handler *pprof,
|
||||
static int thermal_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
u32 out_data;
|
||||
|
@ -1094,7 +1063,7 @@ static int thermal_profile_get(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_profile_set(struct platform_profile_handler *pprof,
|
||||
static int thermal_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
if (quirks->gmode) {
|
||||
|
@ -1120,13 +1089,13 @@ 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 thermal_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
u32 out_data;
|
||||
enum platform_profile_option profile;
|
||||
enum wmax_thermal_mode mode;
|
||||
u8 sys_desc[4];
|
||||
u32 first_mode;
|
||||
enum wmax_thermal_mode mode;
|
||||
enum platform_profile_option profile;
|
||||
u32 out_data;
|
||||
int ret;
|
||||
|
||||
ret = wmax_thermal_information(WMAX_OPERATION_SYS_DESCRIPTION,
|
||||
|
@ -1153,31 +1122,56 @@ static int create_thermal_profile(void)
|
|||
profile = wmax_mode_to_platform_profile[mode];
|
||||
supported_thermal_profiles[profile] = out_data;
|
||||
|
||||
set_bit(profile, pp_handler.choices);
|
||||
set_bit(profile, choices);
|
||||
}
|
||||
|
||||
if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST))
|
||||
if (bitmap_empty(choices, PLATFORM_PROFILE_LAST))
|
||||
return -ENODEV;
|
||||
|
||||
if (quirks->gmode) {
|
||||
supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
|
||||
WMAX_THERMAL_MODE_GMODE;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
}
|
||||
|
||||
pp_handler.profile_get = thermal_profile_get;
|
||||
pp_handler.profile_set = thermal_profile_set;
|
||||
|
||||
return platform_profile_register(&pp_handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_thermal_profile(void)
|
||||
static const struct platform_profile_ops awcc_platform_profile_ops = {
|
||||
.probe = thermal_profile_probe,
|
||||
.profile_get = thermal_profile_get,
|
||||
.profile_set = thermal_profile_set,
|
||||
};
|
||||
|
||||
static int create_thermal_profile(struct platform_device *platform_device)
|
||||
{
|
||||
if (quirks->thermal)
|
||||
platform_profile_remove();
|
||||
struct device *ppdev;
|
||||
|
||||
ppdev = devm_platform_profile_register(&platform_device->dev, "alienware-wmi",
|
||||
NULL, &awcc_platform_profile_ops);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ppdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform Driver
|
||||
*/
|
||||
static const struct attribute_group *alienfx_groups[] = {
|
||||
&zone_attribute_group,
|
||||
&hdmi_attribute_group,
|
||||
&lifier_attribute_group,
|
||||
&deepsleep_attribute_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct platform_driver platform_driver = {
|
||||
.driver = {
|
||||
.name = "alienware-wmi",
|
||||
.dev_groups = alienfx_groups,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init alienware_wmi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1217,26 +1211,8 @@ static int __init alienware_wmi_init(void)
|
|||
if (ret)
|
||||
goto fail_platform_device2;
|
||||
|
||||
if (quirks->hdmi_mux > 0) {
|
||||
ret = create_hdmi(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_hdmi;
|
||||
}
|
||||
|
||||
if (quirks->amplifier > 0) {
|
||||
ret = create_amplifier(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_amplifier;
|
||||
}
|
||||
|
||||
if (quirks->deepslp > 0) {
|
||||
ret = create_deepsleep(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_deepsleep;
|
||||
}
|
||||
|
||||
if (quirks->thermal) {
|
||||
ret = create_thermal_profile();
|
||||
ret = create_thermal_profile(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_thermal_profile;
|
||||
}
|
||||
|
@ -1251,11 +1227,7 @@ static int __init alienware_wmi_init(void)
|
|||
|
||||
fail_prep_zones:
|
||||
alienware_zone_exit(platform_device);
|
||||
remove_thermal_profile();
|
||||
fail_prep_thermal_profile:
|
||||
fail_prep_deepsleep:
|
||||
fail_prep_amplifier:
|
||||
fail_prep_hdmi:
|
||||
platform_device_del(platform_device);
|
||||
fail_platform_device2:
|
||||
platform_device_put(platform_device);
|
||||
|
@ -1269,13 +1241,9 @@ module_init(alienware_wmi_init);
|
|||
|
||||
static void __exit alienware_wmi_exit(void)
|
||||
{
|
||||
if (platform_device) {
|
||||
alienware_zone_exit(platform_device);
|
||||
remove_hdmi(platform_device);
|
||||
remove_thermal_profile();
|
||||
platform_device_unregister(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
}
|
||||
alienware_zone_exit(platform_device);
|
||||
platform_device_unregister(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
}
|
||||
|
||||
module_exit(alienware_wmi_exit);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -725,8 +725,8 @@ static void dell_update_rfkill(struct work_struct *ignored)
|
|||
}
|
||||
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
|
||||
|
||||
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
|
||||
void *context)
|
||||
{
|
||||
static bool extended;
|
||||
|
||||
|
@ -884,7 +884,7 @@ static int __init dell_setup_rfkill(void)
|
|||
pr_warn("Unable to register dell rbtn notifier\n");
|
||||
goto err_filter;
|
||||
} else {
|
||||
ret = i8042_install_filter(dell_laptop_i8042_filter);
|
||||
ret = i8042_install_filter(dell_laptop_i8042_filter, NULL);
|
||||
if (ret) {
|
||||
pr_warn("Unable to install key filter\n");
|
||||
goto err_filter;
|
||||
|
|
256
drivers/platform/x86/dell/dell-lis3lv02d.c
Normal file
256
drivers/platform/x86/dell/dell-lis3lv02d.c
Normal file
|
@ -0,0 +1,256 @@
|
|||
// 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 LIS3_WHO_AM_I 0x0f
|
||||
|
||||
#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("Precision M6800", 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 probe_i2c_addr;
|
||||
module_param(probe_i2c_addr, bool, 0444);
|
||||
MODULE_PARM_DESC(probe_i2c_addr, "Probe the i801 I2C bus for the accelerometer on models where the address is unknown, this may be dangerous.");
|
||||
|
||||
static int detect_lis3lv02d(struct i2c_adapter *adap, unsigned short addr)
|
||||
{
|
||||
union i2c_smbus_data smbus_data;
|
||||
int err;
|
||||
|
||||
dev_info(&adap->dev, "Probing for lis3lv02d on address 0x%02x\n", addr);
|
||||
|
||||
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, LIS3_WHO_AM_I,
|
||||
I2C_SMBUS_BYTE_DATA, &smbus_data);
|
||||
if (err < 0)
|
||||
return 0; /* Not found */
|
||||
|
||||
/* valid who-am-i values are from drivers/misc/lis3lv02d/lis3lv02d.c */
|
||||
switch (smbus_data.byte) {
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
case 0x3a:
|
||||
case 0x3b:
|
||||
break;
|
||||
default:
|
||||
dev_warn(&adap->dev, "Unknown who-am-i register value 0x%02x\n",
|
||||
smbus_data.byte);
|
||||
return 0; /* Not found */
|
||||
}
|
||||
|
||||
dev_info(&adap->dev,
|
||||
"Detected lis3lv02d on address 0x%02x, please report this upstream to platform-driver-x86@vger.kernel.org so that a quirk can be added\n",
|
||||
addr);
|
||||
|
||||
return 1; /* Found */
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE);
|
||||
|
||||
if (i2c_addr) {
|
||||
info.addr = i2c_addr;
|
||||
i2c_dev = i2c_new_client_device(adap, &info);
|
||||
} else {
|
||||
/* First try address 0x29 (most used) and then try 0x1d */
|
||||
static const unsigned short addr_list[] = { 0x29, 0x1d, I2C_CLIENT_END };
|
||||
|
||||
i2c_dev = i2c_new_scanned_device(adap, &info, addr_list, detect_lis3lv02d);
|
||||
}
|
||||
|
||||
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 && !probe_i2c_addr) {
|
||||
pr_warn("accelerometer is present on SMBus but its address is unknown, skipping registration\n");
|
||||
pr_info("Pass dell_lis3lv02d.probe_i2c_addr=1 on the kernel command line to probe, this may be dangerous!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lis3lv02d_dmi_id)
|
||||
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,14 @@
|
|||
#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 int supported_modes;
|
||||
|
||||
static const struct dmi_system_id dell_device_table[] __initconst = {
|
||||
{
|
||||
.ident = "Dell Inc.",
|
||||
|
@ -105,8 +109,6 @@ MODULE_DEVICE_TABLE(dmi, dell_device_table);
|
|||
#define DELL_ACC_SET_FIELD GENMASK(11, 8)
|
||||
#define DELL_THERMAL_SUPPORTED GENMASK(3, 0)
|
||||
|
||||
static struct platform_profile_handler *thermal_handler;
|
||||
|
||||
enum thermal_mode_bits {
|
||||
DELL_BALANCED = BIT(0),
|
||||
DELL_COOL_BOTTOM = BIT(1),
|
||||
|
@ -182,7 +184,7 @@ static int thermal_set_mode(enum thermal_mode_bits state)
|
|||
return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
|
||||
}
|
||||
|
||||
static int thermal_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
static int thermal_platform_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
switch (profile) {
|
||||
|
@ -199,7 +201,7 @@ static int thermal_platform_profile_set(struct platform_profile_handler *pprof,
|
|||
}
|
||||
}
|
||||
|
||||
static int thermal_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
static int thermal_platform_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
int ret;
|
||||
|
@ -228,10 +230,30 @@ static int thermal_platform_profile_get(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
if (supported_modes & DELL_QUIET)
|
||||
set_bit(PLATFORM_PROFILE_QUIET, choices);
|
||||
if (supported_modes & DELL_COOL_BOTTOM)
|
||||
set_bit(PLATFORM_PROFILE_COOL, choices);
|
||||
if (supported_modes & DELL_BALANCED)
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
if (supported_modes & DELL_PERFORMANCE)
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops dell_pc_platform_profile_ops = {
|
||||
.probe = thermal_platform_profile_probe,
|
||||
.profile_get = thermal_platform_profile_get,
|
||||
.profile_set = thermal_platform_profile_set,
|
||||
};
|
||||
|
||||
static int thermal_init(void)
|
||||
{
|
||||
struct device *ppdev;
|
||||
int ret;
|
||||
int supported_modes;
|
||||
|
||||
/* If thermal commands are not supported, exit without error */
|
||||
if (!dell_smbios_class_is_supported(CLASS_INFO))
|
||||
|
@ -244,37 +266,28 @@ static int thermal_init(void)
|
|||
if (!supported_modes)
|
||||
return 0;
|
||||
|
||||
thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL);
|
||||
if (!thermal_handler)
|
||||
return -ENOMEM;
|
||||
thermal_handler->profile_get = thermal_platform_profile_get;
|
||||
thermal_handler->profile_set = thermal_platform_profile_set;
|
||||
platform_device = platform_device_register_simple("dell-pc", PLATFORM_DEVID_NONE, NULL, 0);
|
||||
if (IS_ERR(platform_device))
|
||||
return PTR_ERR(platform_device);
|
||||
|
||||
if (supported_modes & DELL_QUIET)
|
||||
set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices);
|
||||
if (supported_modes & DELL_COOL_BOTTOM)
|
||||
set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices);
|
||||
if (supported_modes & DELL_BALANCED)
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices);
|
||||
if (supported_modes & DELL_PERFORMANCE)
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices);
|
||||
|
||||
/* Clean up if failed */
|
||||
ret = platform_profile_register(thermal_handler);
|
||||
if (ret) {
|
||||
kfree(thermal_handler);
|
||||
thermal_handler = NULL;
|
||||
ppdev = devm_platform_profile_register(&platform_device->dev, "dell-pc",
|
||||
NULL, &dell_pc_platform_profile_ops);
|
||||
if (IS_ERR(ppdev)) {
|
||||
ret = PTR_ERR(ppdev);
|
||||
goto cleanup_platform_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_platform_device:
|
||||
platform_device_unregister(platform_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void thermal_cleanup(void)
|
||||
{
|
||||
if (thermal_handler) {
|
||||
platform_profile_remove();
|
||||
kfree(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);
|
||||
|
|
|
@ -25,7 +25,6 @@ struct wmi_sysman_priv wmi_priv = {
|
|||
/* reset bios to defaults */
|
||||
static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
|
||||
static int reset_option = -1;
|
||||
static const struct class *fw_attr_class;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -541,15 +540,11 @@ static int __init sysman_init(void)
|
|||
goto err_exit_bios_attr_pass_interface;
|
||||
}
|
||||
|
||||
ret = fw_attributes_class_get(&fw_attr_class);
|
||||
if (ret)
|
||||
goto err_exit_bios_attr_pass_interface;
|
||||
|
||||
wmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
|
||||
wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
||||
NULL, "%s", DRIVER_NAME);
|
||||
if (IS_ERR(wmi_priv.class_dev)) {
|
||||
ret = PTR_ERR(wmi_priv.class_dev);
|
||||
goto err_unregister_class;
|
||||
goto err_exit_bios_attr_pass_interface;
|
||||
}
|
||||
|
||||
wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL,
|
||||
|
@ -602,10 +597,7 @@ err_release_attributes_data:
|
|||
release_attributes_data();
|
||||
|
||||
err_destroy_classdev:
|
||||
device_destroy(fw_attr_class, MKDEV(0, 0));
|
||||
|
||||
err_unregister_class:
|
||||
fw_attributes_class_put();
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
|
||||
err_exit_bios_attr_pass_interface:
|
||||
exit_bios_attr_pass_interface();
|
||||
|
@ -619,8 +611,7 @@ err_exit_bios_attr_set_interface:
|
|||
static void __exit sysman_exit(void)
|
||||
{
|
||||
release_attributes_data();
|
||||
device_destroy(fw_attr_class, MKDEV(0, 0));
|
||||
fw_attributes_class_put();
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
exit_bios_attr_set_interface();
|
||||
exit_bios_attr_pass_interface();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -2,51 +2,25 @@
|
|||
|
||||
/* Firmware attributes class helper module */
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device/class.h>
|
||||
#include <linux/module.h>
|
||||
#include "firmware_attributes_class.h"
|
||||
|
||||
static DEFINE_MUTEX(fw_attr_lock);
|
||||
static int fw_attr_inuse;
|
||||
|
||||
static const struct class firmware_attributes_class = {
|
||||
const struct class firmware_attributes_class = {
|
||||
.name = "firmware-attributes",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(firmware_attributes_class);
|
||||
|
||||
int fw_attributes_class_get(const struct class **fw_attr_class)
|
||||
static __init int fw_attributes_class_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&fw_attr_lock);
|
||||
if (!fw_attr_inuse) { /*first time class is being used*/
|
||||
err = class_register(&firmware_attributes_class);
|
||||
if (err) {
|
||||
mutex_unlock(&fw_attr_lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
fw_attr_inuse++;
|
||||
*fw_attr_class = &firmware_attributes_class;
|
||||
mutex_unlock(&fw_attr_lock);
|
||||
return 0;
|
||||
return class_register(&firmware_attributes_class);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fw_attributes_class_get);
|
||||
module_init(fw_attributes_class_init);
|
||||
|
||||
int fw_attributes_class_put(void)
|
||||
static __exit void fw_attributes_class_exit(void)
|
||||
{
|
||||
mutex_lock(&fw_attr_lock);
|
||||
if (!fw_attr_inuse) {
|
||||
mutex_unlock(&fw_attr_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
fw_attr_inuse--;
|
||||
if (!fw_attr_inuse) /* No more consumers */
|
||||
class_unregister(&firmware_attributes_class);
|
||||
mutex_unlock(&fw_attr_lock);
|
||||
return 0;
|
||||
class_unregister(&firmware_attributes_class);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fw_attributes_class_put);
|
||||
module_exit(fw_attributes_class_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
|
||||
MODULE_DESCRIPTION("Firmware attributes class helper module");
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
#ifndef FW_ATTR_CLASS_H
|
||||
#define FW_ATTR_CLASS_H
|
||||
|
||||
int fw_attributes_class_get(const struct class **fw_attr_class);
|
||||
int fw_attributes_class_put(void);
|
||||
#include <linux/device/class.h>
|
||||
|
||||
extern const struct class firmware_attributes_class;
|
||||
|
||||
#endif /* FW_ATTR_CLASS_H */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -24,8 +24,6 @@ struct bioscfg_priv bioscfg_drv = {
|
|||
.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
|
||||
};
|
||||
|
||||
static const struct class *fw_attr_class;
|
||||
|
||||
ssize_t display_name_language_code_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
|
@ -972,11 +970,7 @@ static int __init hp_init(void)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fw_attributes_class_get(&fw_attr_class);
|
||||
if (ret)
|
||||
goto err_unregister_class;
|
||||
|
||||
bioscfg_drv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
|
||||
bioscfg_drv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
||||
NULL, "%s", DRIVER_NAME);
|
||||
if (IS_ERR(bioscfg_drv.class_dev)) {
|
||||
ret = PTR_ERR(bioscfg_drv.class_dev);
|
||||
|
@ -1043,10 +1037,9 @@ err_release_attributes_data:
|
|||
release_attributes_data();
|
||||
|
||||
err_destroy_classdev:
|
||||
device_destroy(fw_attr_class, MKDEV(0, 0));
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
|
||||
err_unregister_class:
|
||||
fw_attributes_class_put();
|
||||
hp_exit_attr_set_interface();
|
||||
|
||||
return ret;
|
||||
|
@ -1055,9 +1048,8 @@ err_unregister_class:
|
|||
static void __exit hp_exit(void)
|
||||
{
|
||||
release_attributes_data();
|
||||
device_destroy(fw_attr_class, MKDEV(0, 0));
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
|
||||
fw_attributes_class_put();
|
||||
hp_exit_attr_set_interface();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,10 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
|
|||
#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
|
||||
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
|
||||
|
||||
#define HP_FAN_SPEED_AUTOMATIC 0x00
|
||||
#define HP_POWER_LIMIT_DEFAULT 0x00
|
||||
#define HP_POWER_LIMIT_NO_CHANGE 0xFF
|
||||
|
||||
#define ACPI_AC_CLASS "ac_adapter"
|
||||
|
||||
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
|
||||
|
@ -83,11 +87,16 @@ static const char * const omen_timed_thermal_profile_boards[] = {
|
|||
"8BAD", "8A42", "8A15"
|
||||
};
|
||||
|
||||
/* DMI Board names of Victus laptops */
|
||||
/* DMI Board names of Victus 16-d1xxx laptops */
|
||||
static const char * const victus_thermal_profile_boards[] = {
|
||||
"8A25"
|
||||
};
|
||||
|
||||
/* DMI Board names of Victus 16-s1000 laptops */
|
||||
static const char * const victus_s_thermal_profile_boards[] = {
|
||||
"8C9C"
|
||||
};
|
||||
|
||||
enum hp_wmi_radio {
|
||||
HPWMI_WIFI = 0x0,
|
||||
HPWMI_BLUETOOTH = 0x1,
|
||||
|
@ -147,12 +156,32 @@ enum hp_wmi_commandtype {
|
|||
HPWMI_THERMAL_PROFILE_QUERY = 0x4c,
|
||||
};
|
||||
|
||||
struct victus_power_limits {
|
||||
u8 pl1;
|
||||
u8 pl2;
|
||||
u8 pl4;
|
||||
u8 cpu_gpu_concurrent_limit;
|
||||
};
|
||||
|
||||
struct victus_gpu_power_modes {
|
||||
u8 ctgp_enable;
|
||||
u8 ppab_enable;
|
||||
u8 dstate;
|
||||
u8 gpu_slowdown_temp;
|
||||
};
|
||||
|
||||
enum hp_wmi_gm_commandtype {
|
||||
HPWMI_FAN_SPEED_GET_QUERY = 0x11,
|
||||
HPWMI_SET_PERFORMANCE_MODE = 0x1A,
|
||||
HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
|
||||
HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
|
||||
HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28,
|
||||
HPWMI_FAN_SPEED_GET_QUERY = 0x11,
|
||||
HPWMI_SET_PERFORMANCE_MODE = 0x1A,
|
||||
HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
|
||||
HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
|
||||
HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28,
|
||||
HPWMI_FAN_COUNT_GET_QUERY = 0x10,
|
||||
HPWMI_GET_GPU_THERMAL_MODES_QUERY = 0x21,
|
||||
HPWMI_SET_GPU_THERMAL_MODES_QUERY = 0x22,
|
||||
HPWMI_SET_POWER_LIMITS_QUERY = 0x29,
|
||||
HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY = 0x2D,
|
||||
HPWMI_FAN_SPEED_SET_QUERY = 0x2E,
|
||||
};
|
||||
|
||||
enum hp_wmi_command {
|
||||
|
@ -211,6 +240,11 @@ enum hp_thermal_profile_victus {
|
|||
HP_VICTUS_THERMAL_PROFILE_QUIET = 0x03,
|
||||
};
|
||||
|
||||
enum hp_thermal_profile_victus_s {
|
||||
HP_VICTUS_S_THERMAL_PROFILE_DEFAULT = 0x00,
|
||||
HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE = 0x01,
|
||||
};
|
||||
|
||||
enum hp_thermal_profile {
|
||||
HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
|
||||
HP_THERMAL_PROFILE_DEFAULT = 0x01,
|
||||
|
@ -273,7 +307,7 @@ static DEFINE_MUTEX(active_platform_profile_lock);
|
|||
static struct input_dev *hp_wmi_input_dev;
|
||||
static struct input_dev *camera_shutter_input_dev;
|
||||
static struct platform_device *hp_wmi_platform_dev;
|
||||
static struct platform_profile_handler platform_profile_handler;
|
||||
static struct device *platform_profile_device;
|
||||
static struct notifier_block platform_power_source_nb;
|
||||
static enum platform_profile_option active_platform_profile;
|
||||
static bool platform_profile_support;
|
||||
|
@ -411,6 +445,26 @@ out_free:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calling this hp_wmi_get_fan_count_userdefine_trigger function also enables
|
||||
* and/or maintains the laptop in user defined thermal and fan states, instead
|
||||
* of using a fallback state. After a 120 seconds timeout however, the laptop
|
||||
* goes back to its fallback state.
|
||||
*/
|
||||
static int hp_wmi_get_fan_count_userdefine_trigger(void)
|
||||
{
|
||||
u8 fan_data[4] = {};
|
||||
int ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_FAN_COUNT_GET_QUERY, HPWMI_GM,
|
||||
&fan_data, sizeof(u8),
|
||||
sizeof(fan_data));
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return fan_data[0]; /* Others bytes aren't providing fan count */
|
||||
}
|
||||
|
||||
static int hp_wmi_get_fan_speed(int fan)
|
||||
{
|
||||
u8 fsh, fsl;
|
||||
|
@ -429,6 +483,23 @@ static int hp_wmi_get_fan_speed(int fan)
|
|||
return (fsh << 8) | fsl;
|
||||
}
|
||||
|
||||
static int hp_wmi_get_fan_speed_victus_s(int fan)
|
||||
{
|
||||
u8 fan_data[128] = {};
|
||||
int ret;
|
||||
|
||||
if (fan < 0 || fan >= sizeof(fan_data))
|
||||
return -EINVAL;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY,
|
||||
HPWMI_GM, &fan_data, sizeof(u8),
|
||||
sizeof(fan_data));
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return fan_data[fan] * 100;
|
||||
}
|
||||
|
||||
static int hp_wmi_read_int(int query)
|
||||
{
|
||||
int val = 0, ret;
|
||||
|
@ -557,6 +628,30 @@ static int hp_wmi_fan_speed_max_set(int enabled)
|
|||
return enabled;
|
||||
}
|
||||
|
||||
static int hp_wmi_fan_speed_reset(void)
|
||||
{
|
||||
u8 fan_speed[2] = { HP_FAN_SPEED_AUTOMATIC, HP_FAN_SPEED_AUTOMATIC };
|
||||
int ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_SET_QUERY, HPWMI_GM,
|
||||
&fan_speed, sizeof(fan_speed), 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hp_wmi_fan_speed_max_reset(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hp_wmi_fan_speed_max_set(0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disabling max fan speed on Victus s1xxx laptops needs a 2nd step: */
|
||||
ret = hp_wmi_fan_speed_reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hp_wmi_fan_speed_max_get(void)
|
||||
{
|
||||
int val = 0, ret;
|
||||
|
@ -1221,7 +1316,7 @@ static int platform_profile_omen_get_ec(enum platform_profile_option *profile)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_get(struct platform_profile_handler *pprof,
|
||||
static int platform_profile_omen_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
/*
|
||||
|
@ -1318,7 +1413,7 @@ static int platform_profile_omen_set_ec(enum platform_profile_option profile)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
|
||||
static int platform_profile_omen_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err;
|
||||
|
@ -1345,7 +1440,7 @@ static int thermal_profile_set(int thermal_profile)
|
|||
sizeof(thermal_profile), 0);
|
||||
}
|
||||
|
||||
static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
static int hp_wmi_platform_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
int tp;
|
||||
|
@ -1374,7 +1469,7 @@ static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
static int hp_wmi_platform_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err, tp;
|
||||
|
@ -1440,11 +1535,11 @@ static int platform_profile_victus_get_ec(enum platform_profile_option *profile)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_get(struct platform_profile_handler *pprof,
|
||||
static int platform_profile_victus_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
/* Same behaviour as platform_profile_omen_get */
|
||||
return platform_profile_omen_get(pprof, profile);
|
||||
return platform_profile_omen_get(dev, profile);
|
||||
}
|
||||
|
||||
static int platform_profile_victus_set_ec(enum platform_profile_option profile)
|
||||
|
@ -1472,7 +1567,162 @@ static int platform_profile_victus_set_ec(enum platform_profile_option profile)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_set(struct platform_profile_handler *pprof,
|
||||
static bool is_victus_s_thermal_profile(void)
|
||||
{
|
||||
const char *board_name;
|
||||
|
||||
board_name = dmi_get_system_info(DMI_BOARD_NAME);
|
||||
if (!board_name)
|
||||
return false;
|
||||
|
||||
return match_string(victus_s_thermal_profile_boards,
|
||||
ARRAY_SIZE(victus_s_thermal_profile_boards),
|
||||
board_name) >= 0;
|
||||
}
|
||||
|
||||
static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
|
||||
bool *ppab_enable,
|
||||
u8 *dstate,
|
||||
u8 *gpu_slowdown_temp)
|
||||
{
|
||||
struct victus_gpu_power_modes gpu_power_modes;
|
||||
int ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_GET_GPU_THERMAL_MODES_QUERY, HPWMI_GM,
|
||||
&gpu_power_modes, sizeof(gpu_power_modes),
|
||||
sizeof(gpu_power_modes));
|
||||
if (ret == 0) {
|
||||
*ctgp_enable = gpu_power_modes.ctgp_enable ? true : false;
|
||||
*ppab_enable = gpu_power_modes.ppab_enable ? true : false;
|
||||
*dstate = gpu_power_modes.dstate;
|
||||
*gpu_slowdown_temp = gpu_power_modes.gpu_slowdown_temp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int victus_s_gpu_thermal_profile_set(bool ctgp_enable,
|
||||
bool ppab_enable,
|
||||
u8 dstate)
|
||||
{
|
||||
struct victus_gpu_power_modes gpu_power_modes;
|
||||
int ret;
|
||||
|
||||
bool current_ctgp_state, current_ppab_state;
|
||||
u8 current_dstate, current_gpu_slowdown_temp;
|
||||
|
||||
/* Retrieving GPU slowdown temperature, in order to keep it unchanged */
|
||||
ret = victus_s_gpu_thermal_profile_get(¤t_ctgp_state,
|
||||
¤t_ppab_state,
|
||||
¤t_dstate,
|
||||
¤t_gpu_slowdown_temp);
|
||||
if (ret < 0) {
|
||||
pr_warn("GPU modes not updated, unable to get slowdown temp\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpu_power_modes.ctgp_enable = ctgp_enable ? 0x01 : 0x00;
|
||||
gpu_power_modes.ppab_enable = ppab_enable ? 0x01 : 0x00;
|
||||
gpu_power_modes.dstate = dstate;
|
||||
gpu_power_modes.gpu_slowdown_temp = current_gpu_slowdown_temp;
|
||||
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_SET_GPU_THERMAL_MODES_QUERY, HPWMI_GM,
|
||||
&gpu_power_modes, sizeof(gpu_power_modes), 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Note: HP_POWER_LIMIT_DEFAULT can be used to restore default PL1 and PL2 */
|
||||
static int victus_s_set_cpu_pl1_pl2(u8 pl1, u8 pl2)
|
||||
{
|
||||
struct victus_power_limits power_limits;
|
||||
int ret;
|
||||
|
||||
/* We need to know both PL1 and PL2 values in order to check them */
|
||||
if (pl1 == HP_POWER_LIMIT_NO_CHANGE || pl2 == HP_POWER_LIMIT_NO_CHANGE)
|
||||
return -EINVAL;
|
||||
|
||||
/* PL2 is not supposed to be lower than PL1 */
|
||||
if (pl2 < pl1)
|
||||
return -EINVAL;
|
||||
|
||||
power_limits.pl1 = pl1;
|
||||
power_limits.pl2 = pl2;
|
||||
power_limits.pl4 = HP_POWER_LIMIT_NO_CHANGE;
|
||||
power_limits.cpu_gpu_concurrent_limit = HP_POWER_LIMIT_NO_CHANGE;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_SET_POWER_LIMITS_QUERY, HPWMI_GM,
|
||||
&power_limits, sizeof(power_limits), 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_s_set_ec(enum platform_profile_option profile)
|
||||
{
|
||||
bool gpu_ctgp_enable, gpu_ppab_enable;
|
||||
u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */
|
||||
int err, tp;
|
||||
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
tp = HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE;
|
||||
gpu_ctgp_enable = true;
|
||||
gpu_ppab_enable = true;
|
||||
gpu_dstate = 1;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT;
|
||||
gpu_ctgp_enable = false;
|
||||
gpu_ppab_enable = true;
|
||||
gpu_dstate = 1;
|
||||
break;
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT;
|
||||
gpu_ctgp_enable = false;
|
||||
gpu_ppab_enable = false;
|
||||
gpu_dstate = 1;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
hp_wmi_get_fan_count_userdefine_trigger();
|
||||
|
||||
err = omen_thermal_profile_set(tp);
|
||||
if (err < 0) {
|
||||
pr_err("Failed to set platform profile %d: %d\n", profile, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = victus_s_gpu_thermal_profile_set(gpu_ctgp_enable,
|
||||
gpu_ppab_enable,
|
||||
gpu_dstate);
|
||||
if (err < 0) {
|
||||
pr_err("Failed to set GPU profile %d: %d\n", profile, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_s_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err;
|
||||
|
||||
guard(mutex)(&active_platform_profile_lock);
|
||||
|
||||
err = platform_profile_victus_s_set_ec(profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
active_platform_profile = profile;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err;
|
||||
|
@ -1488,6 +1738,26 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hp_wmi_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
if (is_omen_thermal_profile()) {
|
||||
set_bit(PLATFORM_PROFILE_COOL, choices);
|
||||
} else if (is_victus_thermal_profile()) {
|
||||
set_bit(PLATFORM_PROFILE_QUIET, choices);
|
||||
} else if (is_victus_s_thermal_profile()) {
|
||||
/* Adding an equivalent to HP Omen software ECO mode: */
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
} else {
|
||||
set_bit(PLATFORM_PROFILE_QUIET, choices);
|
||||
set_bit(PLATFORM_PROFILE_COOL, choices);
|
||||
}
|
||||
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omen_powersource_event(struct notifier_block *nb,
|
||||
unsigned long value,
|
||||
void *data)
|
||||
|
@ -1545,6 +1815,39 @@ static int omen_powersource_event(struct notifier_block *nb,
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int victus_s_powersource_event(struct notifier_block *nb,
|
||||
unsigned long value,
|
||||
void *data)
|
||||
{
|
||||
struct acpi_bus_event *event_entry = data;
|
||||
int err;
|
||||
|
||||
if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
pr_debug("Received power source device event\n");
|
||||
|
||||
/*
|
||||
* Switching to battery power source while Performance mode is active
|
||||
* needs manual triggering of CPU power limits. Same goes when switching
|
||||
* to AC power source while Performance mode is active. Other modes
|
||||
* however are automatically behaving without any manual action.
|
||||
* Seen on HP 16-s1034nf (board 8C9C) with F.11 and F.13 BIOS versions.
|
||||
*/
|
||||
|
||||
if (active_platform_profile == PLATFORM_PROFILE_PERFORMANCE) {
|
||||
pr_debug("Triggering CPU PL1/PL2 actualization\n");
|
||||
err = victus_s_set_cpu_pl1_pl2(HP_POWER_LIMIT_DEFAULT,
|
||||
HP_POWER_LIMIT_DEFAULT);
|
||||
if (err)
|
||||
pr_warn("Failed to actualize power limits: %d\n", err);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int omen_register_powersource_event_handler(void)
|
||||
{
|
||||
int err;
|
||||
|
@ -1560,13 +1863,57 @@ static int omen_register_powersource_event_handler(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int victus_s_register_powersource_event_handler(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
platform_power_source_nb.notifier_call = victus_s_powersource_event;
|
||||
err = register_acpi_notifier(&platform_power_source_nb);
|
||||
if (err < 0) {
|
||||
pr_warn("Failed to install ACPI power source notify handler\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void omen_unregister_powersource_event_handler(void)
|
||||
{
|
||||
unregister_acpi_notifier(&platform_power_source_nb);
|
||||
}
|
||||
|
||||
static int thermal_profile_setup(void)
|
||||
static inline void victus_s_unregister_powersource_event_handler(void)
|
||||
{
|
||||
unregister_acpi_notifier(&platform_power_source_nb);
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops platform_profile_omen_ops = {
|
||||
.probe = hp_wmi_platform_profile_probe,
|
||||
.profile_get = platform_profile_omen_get,
|
||||
.profile_set = platform_profile_omen_set,
|
||||
};
|
||||
|
||||
static const struct platform_profile_ops platform_profile_victus_ops = {
|
||||
.probe = hp_wmi_platform_profile_probe,
|
||||
.profile_get = platform_profile_victus_get,
|
||||
.profile_set = platform_profile_victus_set,
|
||||
};
|
||||
|
||||
static const struct platform_profile_ops platform_profile_victus_s_ops = {
|
||||
.probe = hp_wmi_platform_profile_probe,
|
||||
.profile_get = platform_profile_omen_get,
|
||||
.profile_set = platform_profile_victus_s_set,
|
||||
};
|
||||
|
||||
static const struct platform_profile_ops hp_wmi_platform_profile_ops = {
|
||||
.probe = hp_wmi_platform_profile_probe,
|
||||
.profile_get = hp_wmi_platform_profile_get,
|
||||
.profile_set = hp_wmi_platform_profile_set,
|
||||
};
|
||||
|
||||
static int thermal_profile_setup(struct platform_device *device)
|
||||
{
|
||||
const struct platform_profile_ops *ops;
|
||||
int err, tp;
|
||||
|
||||
if (is_omen_thermal_profile()) {
|
||||
|
@ -1582,10 +1929,7 @@ static int thermal_profile_setup(void)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.profile_get = platform_profile_omen_get;
|
||||
platform_profile_handler.profile_set = platform_profile_omen_set;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
|
||||
ops = &platform_profile_omen_ops;
|
||||
} else if (is_victus_thermal_profile()) {
|
||||
err = platform_profile_victus_get_ec(&active_platform_profile);
|
||||
if (err < 0)
|
||||
|
@ -1599,10 +1943,19 @@ static int thermal_profile_setup(void)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.profile_get = platform_profile_victus_get;
|
||||
platform_profile_handler.profile_set = platform_profile_victus_set;
|
||||
ops = &platform_profile_victus_ops;
|
||||
} else if (is_victus_s_thermal_profile()) {
|
||||
/*
|
||||
* Being unable to retrieve laptop's current thermal profile,
|
||||
* during this setup, we set it to Balanced by default.
|
||||
*/
|
||||
active_platform_profile = PLATFORM_PROFILE_BALANCED;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices);
|
||||
err = platform_profile_victus_s_set_ec(active_platform_profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ops = &platform_profile_victus_s_ops;
|
||||
} else {
|
||||
tp = thermal_profile_get();
|
||||
|
||||
|
@ -1617,20 +1970,15 @@ static int thermal_profile_setup(void)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.profile_get = hp_wmi_platform_profile_get;
|
||||
platform_profile_handler.profile_set = hp_wmi_platform_profile_set;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
|
||||
ops = &hp_wmi_platform_profile_ops;
|
||||
}
|
||||
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices);
|
||||
|
||||
err = platform_profile_register(&platform_profile_handler);
|
||||
if (err)
|
||||
return err;
|
||||
platform_profile_device = devm_platform_profile_register(&device->dev, "hp-wmi",
|
||||
NULL, ops);
|
||||
if (IS_ERR(platform_profile_device))
|
||||
return PTR_ERR(platform_profile_device);
|
||||
|
||||
pr_info("Registered as platform profile handler\n");
|
||||
platform_profile_support = true;
|
||||
|
||||
return 0;
|
||||
|
@ -1663,7 +2011,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;
|
||||
}
|
||||
|
@ -1689,9 +2037,6 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device)
|
|||
rfkill_unregister(wwan_rfkill);
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
}
|
||||
|
||||
if (platform_profile_support)
|
||||
platform_profile_remove();
|
||||
}
|
||||
|
||||
static int hp_wmi_resume_handler(struct device *device)
|
||||
|
@ -1759,8 +2104,13 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
|
|||
case hwmon_pwm:
|
||||
return 0644;
|
||||
case hwmon_fan:
|
||||
if (hp_wmi_get_fan_speed(channel) >= 0)
|
||||
return 0444;
|
||||
if (is_victus_s_thermal_profile()) {
|
||||
if (hp_wmi_get_fan_speed_victus_s(channel) >= 0)
|
||||
return 0444;
|
||||
} else {
|
||||
if (hp_wmi_get_fan_speed(channel) >= 0)
|
||||
return 0444;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
@ -1776,8 +2126,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
ret = hp_wmi_get_fan_speed(channel);
|
||||
|
||||
if (is_victus_s_thermal_profile())
|
||||
ret = hp_wmi_get_fan_speed_victus_s(channel);
|
||||
else
|
||||
ret = hp_wmi_get_fan_speed(channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
|
@ -1810,11 +2162,17 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
|
|||
case hwmon_pwm:
|
||||
switch (val) {
|
||||
case 0:
|
||||
if (is_victus_s_thermal_profile())
|
||||
hp_wmi_get_fan_count_userdefine_trigger();
|
||||
/* 0 is no fan speed control (max), which is 1 for us */
|
||||
return hp_wmi_fan_speed_max_set(1);
|
||||
case 2:
|
||||
/* 2 is automatic speed control, which is 0 for us */
|
||||
return hp_wmi_fan_speed_max_set(0);
|
||||
if (is_victus_s_thermal_profile()) {
|
||||
hp_wmi_get_fan_count_userdefine_trigger();
|
||||
return hp_wmi_fan_speed_max_reset();
|
||||
} else
|
||||
return hp_wmi_fan_speed_max_set(0);
|
||||
default:
|
||||
/* we don't support manual fan speed control */
|
||||
return -EINVAL;
|
||||
|
@ -1893,6 +2251,10 @@ static int __init hp_wmi_init(void)
|
|||
err = omen_register_powersource_event_handler();
|
||||
if (err)
|
||||
goto err_unregister_device;
|
||||
} else if (is_victus_s_thermal_profile()) {
|
||||
err = victus_s_register_powersource_event_handler();
|
||||
if (err)
|
||||
goto err_unregister_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1912,6 +2274,9 @@ static void __exit hp_wmi_exit(void)
|
|||
if (is_omen_thermal_profile() || is_victus_thermal_profile())
|
||||
omen_unregister_powersource_event_handler();
|
||||
|
||||
if (is_victus_s_thermal_profile())
|
||||
victus_s_unregister_powersource_event_handler();
|
||||
|
||||
if (wmi_has_guid(HPWMI_EVENT_GUID))
|
||||
hp_wmi_input_destroy();
|
||||
|
||||
|
|
|
@ -267,7 +267,7 @@ static struct delayed_led_classdev hpled_led = {
|
|||
};
|
||||
|
||||
static bool hp_accel_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
struct serio *port, void *context)
|
||||
{
|
||||
static bool extended;
|
||||
|
||||
|
@ -326,7 +326,7 @@ static int lis3lv02d_probe(struct platform_device *device)
|
|||
/* filter to remove HPQ6000 accelerometer data
|
||||
* from keyboard bus stream */
|
||||
if (strstr(dev_name(&device->dev), "HPQ6000"))
|
||||
i8042_install_filter(hp_accel_i8042_filter);
|
||||
i8042_install_filter(hp_accel_i8042_filter, NULL);
|
||||
|
||||
INIT_WORK(&hpled_led.work, delayed_set_status_worker);
|
||||
ret = led_classdev_register(NULL, &hpled_led.led_classdev);
|
||||
|
|
|
@ -142,7 +142,7 @@ enum {
|
|||
|
||||
struct ideapad_dytc_priv {
|
||||
enum platform_profile_option current_profile;
|
||||
struct platform_profile_handler pprof;
|
||||
struct device *ppdev; /* platform profile device */
|
||||
struct mutex mutex; /* protects the DYTC interface */
|
||||
struct ideapad_private *priv;
|
||||
};
|
||||
|
@ -933,10 +933,10 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe
|
|||
* dytc_profile_get: Function to register with platform_profile
|
||||
* handler. Returns current platform profile.
|
||||
*/
|
||||
static int dytc_profile_get(struct platform_profile_handler *pprof,
|
||||
static int dytc_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
|
||||
struct ideapad_dytc_priv *dytc = dev_get_drvdata(dev);
|
||||
|
||||
*profile = dytc->current_profile;
|
||||
return 0;
|
||||
|
@ -986,10 +986,10 @@ static int dytc_cql_command(struct ideapad_private *priv, unsigned long cmd,
|
|||
* dytc_profile_set: Function to register with platform_profile
|
||||
* handler. Sets current platform profile.
|
||||
*/
|
||||
static int dytc_profile_set(struct platform_profile_handler *pprof,
|
||||
static int dytc_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
|
||||
struct ideapad_dytc_priv *dytc = dev_get_drvdata(dev);
|
||||
struct ideapad_private *priv = dytc->priv;
|
||||
unsigned long output;
|
||||
int err;
|
||||
|
@ -1023,6 +1023,15 @@ static int dytc_profile_set(struct platform_profile_handler *pprof,
|
|||
return -EINTR;
|
||||
}
|
||||
|
||||
static int dytc_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dytc_profile_refresh(struct ideapad_private *priv)
|
||||
{
|
||||
enum platform_profile_option profile;
|
||||
|
@ -1041,7 +1050,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->ppdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1063,6 +1072,12 @@ static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct platform_profile_ops dytc_profile_ops = {
|
||||
.probe = dytc_profile_probe,
|
||||
.profile_get = dytc_profile_get,
|
||||
.profile_set = dytc_profile_set,
|
||||
};
|
||||
|
||||
static int ideapad_dytc_profile_init(struct ideapad_private *priv)
|
||||
{
|
||||
int err, dytc_version;
|
||||
|
@ -1103,18 +1118,15 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv)
|
|||
mutex_init(&priv->dytc->mutex);
|
||||
|
||||
priv->dytc->priv = priv;
|
||||
priv->dytc->pprof.profile_get = dytc_profile_get;
|
||||
priv->dytc->pprof.profile_set = dytc_profile_set;
|
||||
|
||||
/* Setup supported modes */
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices);
|
||||
|
||||
/* Create platform_profile structure and register */
|
||||
err = platform_profile_register(&priv->dytc->pprof);
|
||||
if (err)
|
||||
priv->dytc->ppdev = devm_platform_profile_register(&priv->platform_device->dev,
|
||||
"ideapad-laptop", &priv->dytc,
|
||||
&dytc_profile_ops);
|
||||
if (IS_ERR(priv->dytc->ppdev)) {
|
||||
err = PTR_ERR(priv->dytc->ppdev);
|
||||
goto pp_reg_failed;
|
||||
}
|
||||
|
||||
/* Ensure initial values are correct */
|
||||
dytc_profile_refresh(priv);
|
||||
|
@ -1134,7 +1146,6 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
|
|||
if (!priv->dytc)
|
||||
return;
|
||||
|
||||
platform_profile_remove();
|
||||
mutex_destroy(&priv->dytc->mutex);
|
||||
kfree(priv->dytc);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ enum inspur_tmp_profile {
|
|||
|
||||
struct inspur_wmi_priv {
|
||||
struct wmi_device *wdev;
|
||||
struct platform_profile_handler handler;
|
||||
struct device *ppdev;
|
||||
};
|
||||
|
||||
static int inspur_wmi_perform_query(struct wmi_device *wdev,
|
||||
|
@ -84,11 +84,10 @@ out_free:
|
|||
* 0x0: No Error
|
||||
* 0x1: Error
|
||||
*/
|
||||
static int inspur_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
static int inspur_platform_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
|
||||
handler);
|
||||
struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
|
||||
u8 ret_code[4] = {0, 0, 0, 0};
|
||||
int ret;
|
||||
|
||||
|
@ -132,11 +131,10 @@ static int inspur_platform_profile_set(struct platform_profile_handler *pprof,
|
|||
* 0x1: Performance Mode
|
||||
* 0x2: Power Saver Mode
|
||||
*/
|
||||
static int inspur_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
static int inspur_platform_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
|
||||
handler);
|
||||
struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
|
||||
u8 ret_code[4] = {0, 0, 0, 0};
|
||||
int ret;
|
||||
|
||||
|
@ -166,6 +164,21 @@ static int inspur_platform_profile_get(struct platform_profile_handler *pprof,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int inspur_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops inspur_platform_profile_ops = {
|
||||
.probe = inspur_platform_profile_probe,
|
||||
.profile_get = inspur_platform_profile_get,
|
||||
.profile_set = inspur_platform_profile_set,
|
||||
};
|
||||
|
||||
static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
|
||||
{
|
||||
struct inspur_wmi_priv *priv;
|
||||
|
@ -177,19 +190,10 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
|
|||
priv->wdev = wdev;
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
|
||||
priv->handler.profile_get = inspur_platform_profile_get;
|
||||
priv->handler.profile_set = inspur_platform_profile_set;
|
||||
priv->ppdev = devm_platform_profile_register(&wdev->dev, "inspur-wmi", priv,
|
||||
&inspur_platform_profile_ops);
|
||||
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
|
||||
|
||||
return platform_profile_register(&priv->handler);
|
||||
}
|
||||
|
||||
static void inspur_wmi_remove(struct wmi_device *wdev)
|
||||
{
|
||||
platform_profile_remove();
|
||||
return PTR_ERR_OR_ZERO(priv->ppdev);
|
||||
}
|
||||
|
||||
static const struct wmi_device_id inspur_wmi_id_table[] = {
|
||||
|
@ -206,7 +210,6 @@ static struct wmi_driver inspur_wmi_driver = {
|
|||
},
|
||||
.id_table = inspur_wmi_id_table,
|
||||
.probe = inspur_wmi_probe,
|
||||
.remove = inspur_wmi_remove,
|
||||
.no_singleton = true,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -130,39 +130,6 @@ static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_punit_ipc_simple_command() - Simple IPC command
|
||||
* @cmd: IPC command code.
|
||||
* @para1: First 8bit parameter, set 0 if not used.
|
||||
* @para2: Second 8bit parameter, set 0 if not used.
|
||||
*
|
||||
* Send a IPC command to P-Unit when there is no data transaction
|
||||
*
|
||||
* Return: IPC error code or 0 on success.
|
||||
*/
|
||||
int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
|
||||
{
|
||||
IPC_DEV *ipcdev = punit_ipcdev;
|
||||
IPC_TYPE type;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ipcdev->lock);
|
||||
|
||||
reinit_completion(&ipcdev->cmd_complete);
|
||||
type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
|
||||
|
||||
val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
|
||||
val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
|
||||
ipc_write_cmd(ipcdev, type, val);
|
||||
ret = intel_punit_ipc_check_status(ipcdev, type);
|
||||
|
||||
mutex_unlock(&ipcdev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_punit_ipc_simple_command);
|
||||
|
||||
/**
|
||||
* intel_punit_ipc_command() - IPC command with data and pointers
|
||||
* @cmd: IPC command code.
|
||||
|
|
|
@ -398,8 +398,8 @@ free_payload:
|
|||
}
|
||||
|
||||
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 @@ free_buffer:
|
|||
|
||||
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);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wmi.h>
|
||||
#include <linux/cleanup.h>
|
||||
|
||||
#define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
|
||||
|
||||
|
@ -26,10 +27,38 @@ enum {
|
|||
SW_CAMERA_ON = 1,
|
||||
};
|
||||
|
||||
static int camera_shutter_input_setup(struct wmi_device *wdev, u8 camera_mode)
|
||||
{
|
||||
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
int err;
|
||||
|
||||
priv->idev = input_allocate_device();
|
||||
if (!priv->idev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->idev->name = "Lenovo WMI Camera Button";
|
||||
priv->idev->phys = "wmi/input0";
|
||||
priv->idev->id.bustype = BUS_HOST;
|
||||
priv->idev->dev.parent = &wdev->dev;
|
||||
|
||||
input_set_capability(priv->idev, EV_SW, SW_CAMERA_LENS_COVER);
|
||||
|
||||
input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
|
||||
camera_mode == SW_CAMERA_ON ? 0 : 1);
|
||||
input_sync(priv->idev);
|
||||
|
||||
err = input_register_device(priv->idev);
|
||||
if (err) {
|
||||
input_free_device(priv->idev);
|
||||
priv->idev = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
|
||||
{
|
||||
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
unsigned int keycode;
|
||||
u8 camera_mode;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
|
@ -53,22 +82,24 @@ static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
|
|||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->notify_lock);
|
||||
guard(mutex)(&priv->notify_lock);
|
||||
|
||||
keycode = camera_mode == SW_CAMERA_ON ?
|
||||
KEY_CAMERA_ACCESS_ENABLE : KEY_CAMERA_ACCESS_DISABLE;
|
||||
input_report_key(priv->idev, keycode, 1);
|
||||
input_sync(priv->idev);
|
||||
input_report_key(priv->idev, keycode, 0);
|
||||
input_sync(priv->idev);
|
||||
if (!priv->idev) {
|
||||
if (camera_shutter_input_setup(wdev, camera_mode))
|
||||
dev_warn(&wdev->dev, "Failed to register input device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock(&priv->notify_lock);
|
||||
if (camera_mode == SW_CAMERA_ON)
|
||||
input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 0);
|
||||
else
|
||||
input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 1);
|
||||
input_sync(priv->idev);
|
||||
}
|
||||
|
||||
static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
|
||||
{
|
||||
struct lenovo_wmi_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
@ -76,21 +107,6 @@ static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
|
|||
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
|
||||
priv->idev = devm_input_allocate_device(&wdev->dev);
|
||||
if (!priv->idev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->idev->name = "Lenovo WMI Camera Button";
|
||||
priv->idev->phys = "wmi/input0";
|
||||
priv->idev->id.bustype = BUS_HOST;
|
||||
priv->idev->dev.parent = &wdev->dev;
|
||||
input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_ENABLE);
|
||||
input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_DISABLE);
|
||||
|
||||
ret = input_register_device(priv->idev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&priv->notify_lock);
|
||||
|
||||
return 0;
|
||||
|
@ -100,6 +116,9 @@ static void lenovo_wmi_remove(struct wmi_device *wdev)
|
|||
{
|
||||
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
if (priv->idev)
|
||||
input_unregister_device(priv->idev);
|
||||
|
||||
mutex_destroy(&priv->notify_lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -806,8 +806,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
|
|||
}
|
||||
static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
|
||||
|
||||
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
|
||||
void *context)
|
||||
{
|
||||
static bool extended;
|
||||
|
||||
|
@ -996,7 +996,7 @@ static int __init load_scm_model_init(struct platform_device *sdev)
|
|||
if (result)
|
||||
goto fail_input;
|
||||
|
||||
result = i8042_install_filter(msi_laptop_i8042_filter);
|
||||
result = i8042_install_filter(msi_laptop_i8042_filter, NULL);
|
||||
if (result) {
|
||||
pr_err("Unable to install key filter\n");
|
||||
goto fail_filter;
|
||||
|
|
|
@ -260,7 +260,7 @@ struct pcc_acpi {
|
|||
* keypress events over the PS/2 kbd interface, filter these out.
|
||||
*/
|
||||
static bool panasonic_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
struct serio *port, void *context)
|
||||
{
|
||||
static bool extended;
|
||||
|
||||
|
@ -1100,7 +1100,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
|
|||
pcc->platform = NULL;
|
||||
}
|
||||
|
||||
i8042_install_filter(panasonic_i8042_filter);
|
||||
i8042_install_filter(panasonic_i8042_filter, NULL);
|
||||
return 0;
|
||||
|
||||
out_platform:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -194,7 +194,6 @@ static const char * const level_options[] = {
|
|||
[TLMI_LEVEL_MASTER] = "master",
|
||||
};
|
||||
static struct think_lmi tlmi_priv;
|
||||
static const struct class *fw_attr_class;
|
||||
static DEFINE_MUTEX(tlmi_mutex);
|
||||
|
||||
static inline struct tlmi_pwd_setting *to_tlmi_pwd_setting(struct kobject *kobj)
|
||||
|
@ -1446,11 +1445,7 @@ static int tlmi_sysfs_init(void)
|
|||
{
|
||||
int i, ret;
|
||||
|
||||
ret = fw_attributes_class_get(&fw_attr_class);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
|
||||
tlmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
||||
NULL, "%s", "thinklmi");
|
||||
if (IS_ERR(tlmi_priv.class_dev)) {
|
||||
ret = PTR_ERR(tlmi_priv.class_dev);
|
||||
|
@ -1563,9 +1558,8 @@ static int tlmi_sysfs_init(void)
|
|||
fail_create_attr:
|
||||
tlmi_release_attr();
|
||||
fail_device_created:
|
||||
device_destroy(fw_attr_class, MKDEV(0, 0));
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
fail_class_created:
|
||||
fw_attributes_class_put();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1788,8 +1782,7 @@ fail_clear_attr:
|
|||
static void tlmi_remove(struct wmi_device *wdev)
|
||||
{
|
||||
tlmi_release_attr();
|
||||
device_destroy(fw_attr_class, MKDEV(0, 0));
|
||||
fw_attributes_class_put();
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
}
|
||||
|
||||
static int tlmi_probe(struct wmi_device *wdev, const void *context)
|
||||
|
|
|
@ -963,6 +963,7 @@ static const struct proc_ops dispatch_proc_ops = {
|
|||
static struct platform_device *tpacpi_pdev;
|
||||
static struct platform_device *tpacpi_sensors_pdev;
|
||||
static struct device *tpacpi_hwmon;
|
||||
static struct device *tpacpi_pprof;
|
||||
static struct input_dev *tpacpi_inputdev;
|
||||
static struct mutex tpacpi_inputdev_send_mutex;
|
||||
static LIST_HEAD(tpacpi_all_drivers);
|
||||
|
@ -3275,6 +3276,7 @@ static const struct key_entry keymap_lenovo[] __initconst = {
|
|||
* scancodes to preserve uAPI compatibility, see tpacpi_input_send_key().
|
||||
*/
|
||||
{ KE_KEY, 0x131d, { KEY_VENDOR } }, /* System debug info, similar to old ThinkPad key */
|
||||
{ KE_KEY, 0x1320, { KEY_LINK_PHONE } },
|
||||
{ KE_KEY, TP_HKEY_EV_TRACK_DOUBLETAP /* 0x8036 */, { KEY_PROG4 } },
|
||||
{ KE_END }
|
||||
};
|
||||
|
@ -10415,7 +10417,7 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe
|
|||
* dytc_profile_get: Function to register with platform_profile
|
||||
* handler. Returns current platform profile.
|
||||
*/
|
||||
static int dytc_profile_get(struct platform_profile_handler *pprof,
|
||||
static int dytc_profile_get(struct device *dev,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
*profile = dytc_current_profile;
|
||||
|
@ -10490,7 +10492,7 @@ static int dytc_cql_command(int command, int *output)
|
|||
* dytc_profile_set: Function to register with platform_profile
|
||||
* handler. Sets current platform profile.
|
||||
*/
|
||||
static int dytc_profile_set(struct platform_profile_handler *pprof,
|
||||
static int dytc_profile_set(struct device *dev,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int perfmode;
|
||||
|
@ -10539,6 +10541,21 @@ unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int dytc_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_profile_ops dytc_profile_ops = {
|
||||
.probe = dytc_profile_probe,
|
||||
.profile_get = dytc_profile_get,
|
||||
.profile_set = dytc_profile_set,
|
||||
};
|
||||
|
||||
static void dytc_profile_refresh(void)
|
||||
{
|
||||
enum platform_profile_option profile;
|
||||
|
@ -10567,24 +10584,14 @@ 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(tpacpi_pprof);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Setup supported modes */
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices);
|
||||
|
||||
err = dytc_command(DYTC_CMD_QUERY, &output);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -10639,12 +10646,13 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
|
|||
"DYTC version %d: thermal mode available\n", dytc_version);
|
||||
|
||||
/* Create platform_profile structure and register */
|
||||
err = platform_profile_register(&dytc_profile);
|
||||
tpacpi_pprof = devm_platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi",
|
||||
NULL, &dytc_profile_ops);
|
||||
/*
|
||||
* If for some reason platform_profiles aren't enabled
|
||||
* don't quit terminally.
|
||||
*/
|
||||
if (err)
|
||||
if (IS_ERR(tpacpi_pprof))
|
||||
return -ENODEV;
|
||||
|
||||
/* Ensure initial values are correct */
|
||||
|
@ -10657,14 +10665,8 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dytc_profile_exit(void)
|
||||
{
|
||||
platform_profile_remove();
|
||||
}
|
||||
|
||||
static struct ibm_struct dytc_profile_driver_data = {
|
||||
.name = "dytc-profile",
|
||||
.exit = dytc_profile_exit,
|
||||
};
|
||||
|
||||
/*************************************************************************
|
||||
|
@ -11681,7 +11683,7 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
|
|||
if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
|
||||
if (strlen(val) > sizeof(ibms_init[i].param) - 1)
|
||||
return -ENOSPC;
|
||||
strcpy(ibms_init[i].param, val);
|
||||
strscpy(ibms_init[i].param, val);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2755,7 +2755,7 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
|
|||
}
|
||||
|
||||
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
struct serio *port, void *context)
|
||||
{
|
||||
if (str & I8042_STR_AUXDATA)
|
||||
return false;
|
||||
|
@ -2915,7 +2915,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
|
|||
if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
|
||||
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
|
||||
|
||||
error = i8042_install_filter(toshiba_acpi_i8042_filter);
|
||||
error = i8042_install_filter(toshiba_acpi_i8042_filter, NULL);
|
||||
if (error) {
|
||||
pr_err("Error installing key filter\n");
|
||||
goto err_free_dev;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -18,10 +18,12 @@
|
|||
* enum sfh_message_type - Query the SFH message type
|
||||
* @MT_HPD: Message ID to know the Human presence info from MP2 FW
|
||||
* @MT_ALS: Message ID to know the Ambient light info from MP2 FW
|
||||
* @MT_SRA: Message ID to know the SRA data from MP2 FW
|
||||
*/
|
||||
enum sfh_message_type {
|
||||
MT_HPD,
|
||||
MT_ALS,
|
||||
MT_SRA,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,10 +42,23 @@ enum sfh_hpd_info {
|
|||
* struct amd_sfh_info - get HPD sensor info from MP2 FW
|
||||
* @ambient_light: Populates the ambient light information
|
||||
* @user_present: Populates the user presence information
|
||||
* @platform_type: Operating modes (clamshell, flat, tent, etc.)
|
||||
* @laptop_placement: Device states (ontable, onlap, outbag)
|
||||
*/
|
||||
struct amd_sfh_info {
|
||||
u32 ambient_light;
|
||||
u8 user_present;
|
||||
u32 platform_type;
|
||||
u32 laptop_placement;
|
||||
};
|
||||
|
||||
enum laptop_placement {
|
||||
LP_UNKNOWN = 0,
|
||||
ON_TABLE,
|
||||
ON_LAP_MOTION,
|
||||
IN_BAG,
|
||||
OUT_OF_BAG,
|
||||
LP_UNDEFINED,
|
||||
};
|
||||
|
||||
int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op);
|
||||
|
|
|
@ -54,15 +54,29 @@
|
|||
|
||||
struct serio;
|
||||
|
||||
/**
|
||||
* typedef i8042_filter_t - i8042 filter callback
|
||||
* @data: Data received by the i8042 controller
|
||||
* @str: Status register of the i8042 controller
|
||||
* @serio: Serio of the i8042 controller
|
||||
* @context: Context pointer associated with this callback
|
||||
*
|
||||
* This represents a i8042 filter callback which can be used with i8042_install_filter()
|
||||
* and i8042_remove_filter() to filter the i8042 input for platform-specific key codes.
|
||||
*
|
||||
* Context: Interrupt context.
|
||||
* Returns: true if the data should be filtered out, false if otherwise.
|
||||
*/
|
||||
typedef bool (*i8042_filter_t)(unsigned char data, unsigned char str, struct serio *serio,
|
||||
void *context);
|
||||
|
||||
#if defined(CONFIG_SERIO_I8042) || defined(CONFIG_SERIO_I8042_MODULE)
|
||||
|
||||
void i8042_lock_chip(void);
|
||||
void i8042_unlock_chip(void);
|
||||
int i8042_command(unsigned char *param, int command);
|
||||
int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio));
|
||||
int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio));
|
||||
int i8042_install_filter(i8042_filter_t filter, void *context);
|
||||
int i8042_remove_filter(i8042_filter_t filter);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -79,14 +93,12 @@ static inline int i8042_command(unsigned char *param, int command)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio))
|
||||
static inline int i8042_install_filter(i8042_filter_t filter, void *context)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
|
||||
struct serio *serio))
|
||||
static inline int i8042_remove_filter(i8042_filter_t filter)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef _PLATFORM_PROFILE_H_
|
||||
#define _PLATFORM_PROFILE_H_
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*
|
||||
|
@ -23,20 +24,34 @@ 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 {
|
||||
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
int (*profile_get)(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile);
|
||||
int (*profile_set)(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile);
|
||||
/**
|
||||
* struct platform_profile_ops - platform profile operations
|
||||
* @probe: Callback to setup choices available to the new class device. These
|
||||
* choices will only be enforced when setting a new profile, not when
|
||||
* getting the current one.
|
||||
* @profile_get: Callback that will be called when showing the current platform
|
||||
* profile in sysfs.
|
||||
* @profile_set: Callback that will be called when storing a new platform
|
||||
* profile in sysfs.
|
||||
*/
|
||||
struct platform_profile_ops {
|
||||
int (*probe)(void *drvdata, unsigned long *choices);
|
||||
int (*profile_get)(struct device *dev, enum platform_profile_option *profile);
|
||||
int (*profile_set)(struct device *dev, enum platform_profile_option profile);
|
||||
};
|
||||
|
||||
int platform_profile_register(struct platform_profile_handler *pprof);
|
||||
int platform_profile_remove(void);
|
||||
struct device *platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops);
|
||||
int platform_profile_remove(struct device *dev);
|
||||
struct device *devm_platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops);
|
||||
int platform_profile_cycle(void);
|
||||
void platform_profile_notify(void);
|
||||
void platform_profile_notify(struct device *dev);
|
||||
|
||||
#endif /*_PLATFORM_PROFILE_H_*/
|
||||
|
|
|
@ -519,6 +519,7 @@
|
|||
#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */
|
||||
#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */
|
||||
#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */
|
||||
#define KEY_LINK_PHONE 0x1bf /* AL Phone Syncing */
|
||||
|
||||
#define KEY_DEL_EOL 0x1c0
|
||||
#define KEY_DEL_EOS 0x1c1
|
||||
|
|
|
@ -16,7 +16,7 @@ struct process_cmd_struct {
|
|||
int arg;
|
||||
};
|
||||
|
||||
static const char *version_str = "v1.20";
|
||||
static const char *version_str = "v1.21";
|
||||
|
||||
static const int supported_api_ver = 3;
|
||||
static struct isst_if_platform_info isst_platform_info;
|
||||
|
|
|
@ -329,7 +329,7 @@ static int tpmi_get_get_trls(struct isst_id *id, int config_index,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_get_trl(struct isst_id *id, int level, int config_index,
|
||||
static int tpmi_get_get_trl(struct isst_id *id, int config_index, int level,
|
||||
int *trl)
|
||||
{
|
||||
struct isst_pkg_ctdp_level_info ctdp_level;
|
||||
|
|
Loading…
Add table
Reference in a new issue