1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

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:
Linus Torvalds 2025-01-24 07:18:39 -08:00
commit c9c0543b52
92 changed files with 3794 additions and 1682 deletions

View 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

View 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.

View file

@ -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.

View file

@ -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.

View file

@ -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
--------------------------

View file

@ -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

View file

@ -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)
{

View file

@ -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) */

View file

@ -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");

View file

@ -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 {

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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;
}

View file

@ -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,

View file

@ -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))
&reg))
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),
&reg))
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, &reg);
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);

View file

@ -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]

View file

@ -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);

View file

@ -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",

View file

@ -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,
&current_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, &current_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);

View file

@ -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,
};

View file

@ -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:

View file

@ -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, \
}

View file

@ -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

View 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;
}

View file

@ -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);

View file

@ -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 */

View file

@ -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

View file

@ -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)

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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 */

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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, &amplifier_attribute_group);
}
static int create_amplifier(struct platform_device *dev)
{
int ret;
ret = sysfs_create_group(&dev->dev.kobj, &amplifier_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,
&amplifier_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);

View file

@ -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)

View file

@ -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;

View file

@ -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;

View 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");

View file

@ -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)

View 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

View file

@ -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,

View file

@ -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);

View file

@ -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();
}

View file

@ -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)

View file

@ -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");

View file

@ -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 */

View file

@ -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 */

View file

@ -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();
}

View file

@ -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(&current_ctgp_state,
&current_ppab_state,
&current_dstate,
&current_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();

View file

@ -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);

View file

@ -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);

View file

@ -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,
};

View file

@ -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.

View file

@ -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);

View file

@ -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;

View file

@ -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)

View file

@ -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");

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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.

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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:

View file

@ -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>

View file

@ -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);
}

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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,

View file

@ -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

View file

@ -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",
},

View file

@ -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;

View file

@ -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
};

View file

@ -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,
};
/*

View 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");

View file

@ -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,

View file

@ -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);

View file

@ -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;
}

View file

@ -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_*/

View file

@ -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

View file

@ -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;

View file

@ -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;