Compare commits

...

30 Commits

Author SHA1 Message Date
David Arcari b2ec0c8f53 platform/x86: ideapad-laptop: pass a correct pointer to the driver data
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit e4d4648eac8b4ef39f412d07715eb26f1ccd7342
Author: Fedor Pchelkin <pchelkin@ispras.ru>
Date:   Tue Jan 28 00:02:01 2025 +0300

    platform/x86: ideapad-laptop: pass a correct pointer to the driver data

    devm_platform_profile_register() expects a pointer to the private driver
    data but instead an address of the pointer variable is passed due to a
    typo. This leads to the crashes later:

    BUG: unable to handle page fault for address: 00000000fe0d0044
    PGD 0 P4D 0
    Oops: Oops: 0000 [#1] PREEMPT SMP NOPTI
    CPU: 6 UID: 0 PID: 1284 Comm: tuned Tainted: G        W          6.13.0+ #7
    Tainted: [W]=WARN
    Hardware name: LENOVO 21D0/LNVNB161216, BIOS J6CN45WW 03/17/2023
    RIP: 0010:__mutex_lock.constprop.0+0x6bf/0x7f0
    Call Trace:
     <TASK>
     dytc_profile_set+0x4a/0x140 [ideapad_laptop]
     _store_and_notify+0x13/0x40 [platform_profile]
     class_for_each_device+0x145/0x180
     platform_profile_store+0xc0/0x130 [platform_profile]
     kernfs_fop_write_iter+0x13e/0x1f0
     vfs_write+0x290/0x450
     ksys_write+0x6c/0xe0
     do_syscall_64+0x82/0x160
     entry_SYSCALL_64_after_hwframe+0x76/0x7e

    Found by Linux Verification Center (linuxtesting.org).

    Fixes: 249c576f0f9d ("ACPI: platform_profile: Let drivers set drvdata to the class device")
    Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
    Reviewed-by: Kurt Borja <kuurtb@gmail.com>
    Link: https://lore.kernel.org/r/20250127210202.568691-1-pchelkin@ispras.ru
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari 2613207f86 ACPI: platform_profile: Avoid initializing on non-ACPI platforms
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit dd133162c9cff5951a692fab9811fadf46a46457
Author: Alexandre Ghiti <alexghiti@rivosinc.com>
Date:   Thu May 22 16:13:56 2025 +0200

    ACPI: platform_profile: Avoid initializing on non-ACPI platforms

    The platform profile driver is loaded even on platforms that do not have
    ACPI enabled. The initialization of the sysfs entries was recently moved
    from platform_profile_register() to the module init call, and those
    entries need acpi_kobj to be initialized which is not the case when ACPI
    is disabled.

    This results in the following warning:

     WARNING: CPU: 5 PID: 1 at fs/sysfs/group.c:131 internal_create_group+0xa22/0xdd8
     Modules linked in:
     CPU: 5 UID: 0 PID: 1 Comm: swapper/0 Tainted: G        W           6.15.0-rc7-dirty #6 PREEMPT
     Tainted: [W]=WARN
     Hardware name: riscv-virtio,qemu (DT)
     epc : internal_create_group+0xa22/0xdd8
      ra : internal_create_group+0xa22/0xdd8

     Call Trace:

     internal_create_group+0xa22/0xdd8
     sysfs_create_group+0x22/0x2e
     platform_profile_init+0x74/0xb2
     do_one_initcall+0x198/0xa9e
     kernel_init_freeable+0x6d8/0x780
     kernel_init+0x28/0x24c
     ret_from_fork+0xe/0x18

    Fix this by checking if ACPI is enabled before trying to create sysfs
    entries.

    Fixes: 77be5cacb2c2 ("ACPI: platform_profile: Create class for ACPI platform profile")
    Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com>
    Reviewed-by: Arnd Bergmann <arnd@arndb.de>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://patch.msgid.link/20250522141410.31315-1-alexghiti@rivosinc.com
    [ rjw: Subject and changelog edits ]
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari 0c339d758b platform/x86/amd: pmf: Simplify error flow in amd_pmf_init_smart_pc()
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit d7186dfd41924ef01ef490be5b82ab63cc417b31
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed May 21 19:34:57 2025 -0500

    platform/x86/amd: pmf: Simplify error flow in amd_pmf_init_smart_pc()

    commit 5b1122fc4995f ("platform/x86/amd/pmf: fix cleanup in
    amd_pmf_init_smart_pc()") adjusted the error handling flow to use a ladder
    but this isn't actually needed because work is only scheduled in
    amd_pmf_start_policy_engine() and with device managed cleanups pointers
    for allocations don't need to be freed.

    Adjust the error flow to a single call to amd_pmf_deinit_smart_pc() for
    the cases that need to clean up.

    Cc: Dan Carpenter <dan.carpenter@linaro.org>
    Link: https://lore.kernel.org/r/20250512211154.2510397-4-superm1@kernel.org
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Link: https://lore.kernel.org/r/20250522003457.1516679-4-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari 44996180ca platform/x86/amd: pmf: Prevent amd_pmf_tee_deinit() from running twice
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 93103d56650d7a38ed37ba4041578310f82776ae
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed May 21 19:34:56 2025 -0500

    platform/x86/amd: pmf: Prevent amd_pmf_tee_deinit() from running twice

    If any of the tee init fails, pass up the errors and clear the tee_ctx
    pointer. This will prevent cleaning up multiple times.

    Fixes: ac052d8c08f9d ("platform/x86/amd/pmf: Add PMF TEE interface")
    Suggested-by: Dan Carpenter <dan.carpenter@linaro.org>
    Link: https://lore.kernel.org/r/20250512211154.2510397-3-superm1@kernel.org
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Link: https://lore.kernel.org/r/20250522003457.1516679-3-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari b05ba06a51 platform/x86/amd: pmf: Use device managed allocations
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit d9db3a941270d92bbd1a6a6b54a10324484f2f2d
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed May 21 19:34:55 2025 -0500

    platform/x86/amd: pmf: Use device managed allocations

    If setting up smart PC fails for any reason then this can lead to
    a double free when unloading amd-pmf.  This is because dev->buf was
    freed but never set to NULL and is again freed in amd_pmf_remove().

    To avoid subtle allocation bugs in failures leading to a double free
    change all allocations into device managed allocations.

    Fixes: 5b1122fc4995f ("platform/x86/amd/pmf: fix cleanup in amd_pmf_init_smart_pc()")
    Link: https://lore.kernel.org/r/20250512211154.2510397-2-superm1@kernel.org
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Link: https://lore.kernel.org/r/20250522003457.1516679-2-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari c57e4b77fc drivers/platform/x86/amd: pmf: Check for invalid Smart PC Policies
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 8e81b9cd6e95188d12c9cc25d40b61dd5ea05ace
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed Apr 23 08:18:45 2025 -0500

    drivers/platform/x86/amd: pmf: Check for invalid Smart PC Policies

    commit 376a8c2a14439 ("platform/x86/amd/pmf: Update PMF Driver for
    Compatibility with new PMF-TA") added support for platforms that support
    an updated TA, however it also exposed a number of platforms that although
    they have support for the updated TA don't actually populate a policy
    binary.

    Add an explicit check that the policy binary isn't empty before
    initializing the TA.

    Reported-by: Christian Heusel <christian@heusel.eu>
    Closes: https://lore.kernel.org/platform-driver-x86/ae644428-5bf2-4b30-81ba-0b259ed3449b@heusel.eu/
    Fixes: 376a8c2a14439 ("platform/x86/amd/pmf: Update PMF Driver for Compatibility with new PMF-TA")
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Tested-by: Christian Heusel <christian@heusel.eu>
    Link: https://lore.kernel.org/r/20250423132002.3984997-3-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari f6af07a496 drivers/platform/x86/amd: pmf: Check for invalid sideloaded Smart PC Policies
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 690d722e02819ef978f90cd7553973eba1007e6c
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed Apr 23 08:18:44 2025 -0500

    drivers/platform/x86/amd: pmf: Check for invalid sideloaded Smart PC Policies

    If a policy is passed into amd_pmf_get_pb_data() that causes the engine
    to fail to start there is a memory leak. Free the memory in this failure
    path.

    Fixes: 10817f28e5337 ("platform/x86/amd/pmf: Add capability to sideload of policy binary")
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Link: https://lore.kernel.org/r/20250423132002.3984997-2-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:32 -04:00
David Arcari 93d900a094 platform/x86: amd: pmf: Fix STT limits
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit fcf27a6a926fd9eeba39e9c3fde43c9298fe284e
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Mon Apr 7 13:18:21 2025 -0500

    platform/x86: amd: pmf: Fix STT limits

    On some platforms it has been observed that STT limits are not being
    applied properly causing poor performance as power limits are set too low.

    STT limits that are sent to the platform are supposed to be in Q8.8
    format.  Convert them before sending.

    Reported-by: Yijun Shen <Yijun.Shen@dell.com>
    Fixes: 7c45534afa443 ("platform/x86/amd/pmf: Add support for PMF Policy Binary")
    Cc: stable@vger.kernel.org
    Tested-by: Yijun Shen <Yijun_Shen@Dell.com>
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Acked-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Link: https://lore.kernel.org/r/20250407181915.1482450-1-superm1@kernel.org
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari f5e52a6076 platform/x86/amd/pmf: convert timeouts to secs_to_jiffies()
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 96b8f4658b70ac2efe543ddbeb328b5b1941a571
Author: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Date:   Tue Feb 25 20:17:28 2025 +0000

    platform/x86/amd/pmf: convert timeouts to secs_to_jiffies()

    Commit b35108a51cf7 ("jiffies: Define secs_to_jiffies()") introduced
    secs_to_jiffies().  As the value here is a multiple of 1000, use
    secs_to_jiffies() instead of msecs_to_jiffies() to avoid the multiplication

    This is converted using scripts/coccinelle/misc/secs_to_jiffies.cocci with
    the following Coccinelle rules:

    @depends on patch@
    expression E;
    @@

    -msecs_to_jiffies
    +secs_to_jiffies
    (E
    - * \( 1000 \| MSEC_PER_SEC \)
    )

    Signed-off-by: Easwar Hariharan <eahariha@linux.microsoft.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Link: https://lore.kernel.org/r/20250225-converge-secs-to-jiffies-part-two-v3-14-a43967e36c88@linux.microsoft.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari 45f2db52c6 platform/x86/amd: pmf: Fix missing hidden options for Smart PC
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 4490fe973669360efaef7350aeb9706f70164176
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Wed Mar 5 21:44:02 2025 -0600

    platform/x86/amd: pmf: Fix missing hidden options for Smart PC

    amd_pmf_get_slider_info() checks the current profile to report correct
    value to the TA inputs.  If hidden options are in use then the wrong
    values will be reported to TA.

    Add the two compat options PLATFORM_PROFILE_BALANCED_PERFORMANCE and
    PLATFORM_PROFILE_QUIET for this use.

    Reported-by: Yijun Shen <Yijun.Shen@dell.com>
    Fixes: 9a43102daf64d ("platform/x86/amd: pmf: Add balanced-performance to hidden choices")
    Fixes: 44e94fece5170 ("platform/x86/amd: pmf: Add 'quiet' to hidden choices")
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Acked-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
    Link: https://lore.kernel.org/r/20250306034402.50478-1-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari f253124ccc platform/x86/amd/pmf: fix cleanup in amd_pmf_init_smart_pc()
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 5b1122fc4995f308b21d7cfc64ef9880ac834d20
Author: Dan Carpenter <dan.carpenter@linaro.org>
Date:   Mon Mar 10 22:48:29 2025 +0300

    platform/x86/amd/pmf: fix cleanup in amd_pmf_init_smart_pc()

    There are a few problems in this code:

    First, if amd_pmf_tee_init() fails then the function returns directly
    instead of cleaning up.  We cannot simply do a "goto error;" because
    the amd_pmf_tee_init() cleanup calls tee_shm_free(dev->fw_shm_pool);
    and amd_pmf_tee_deinit() calls it as well leading to a double free.
    I have re-written this code to use an unwind ladder to free the
    allocations.

    Second, if amd_pmf_start_policy_engine() fails on every iteration though
    the loop then the code calls amd_pmf_tee_deinit() twice which is also a
    double free.  Call amd_pmf_tee_deinit() inside the loop for each failed
    iteration.  Also on that path the error codes are not necessarily
    negative kernel error codes.  Set the error code to -EINVAL.

    There is a very subtle third bug which is that if the call to
    input_register_device() in amd_pmf_register_input_device() fails then
    we call input_unregister_device() on an input device that wasn't
    registered.  This will lead to a reference counting underflow
    because of the device_del(&dev->dev) in __input_unregister_device().
    It's unlikely that anyone would ever hit this bug in real life.

    Fixes: 376a8c2a1443 ("platform/x86/amd/pmf: Update PMF Driver for Compatibility with new PMF-TA")
    Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
    Link: https://lore.kernel.org/r/232231fc-6a71-495e-971b-be2a76f6db4c@stanley.mountain
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari 9c9e50d974 platform/x86/amd/pmf: Update PMF Driver for Compatibility with new PMF-TA
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 376a8c2a144397d9cf2a67d403dd64f4a7ff9104
Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
Date:   Wed Mar 5 10:28:42 2025 +0530

    platform/x86/amd/pmf: Update PMF Driver for Compatibility with new PMF-TA

    The PMF driver allocates a shared memory buffer using
    tee_shm_alloc_kernel_buf() for communication with the PMF-TA.

    The latest PMF-TA version introduces new structures with OEM debug
    information and additional policy input conditions for evaluating the
    policy binary. Consequently, the shared memory size must be increased to
    ensure compatibility between the PMF driver and the updated PMF-TA.

    To do so, introduce the new PMF-TA UUID and update the PMF shared memory
    configuration to ensure compatibility with the latest PMF-TA version.
    Additionally, export the TA UUID.

    These updates will result in modifications to the prototypes of
    amd_pmf_tee_init() and amd_pmf_ta_open_session().

    Link: https://lore.kernel.org/all/55ac865f-b1c7-fa81-51c4-d211c7963e7e@linux.intel.com/
    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
    Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
    Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
    Link: https://lore.kernel.org/r/20250305045842.4117767-2-Shyam-sundar.S-k@amd.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari 3c62a3823f platform/x86/amd/pmf: Propagate PMF-TA return codes
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 9ba93cb8212d62bccd8b41b8adb6656abf37280a
Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
Date:   Wed Mar 5 10:28:41 2025 +0530

    platform/x86/amd/pmf: Propagate PMF-TA return codes

    In the amd_pmf_invoke_cmd_init() function within the PMF driver ensure
    that the actual result from the PMF-TA is returned rather than a generic
    EIO. This change allows for proper handling of errors originating from the
    PMF-TA.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
    Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
    Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
    Link: https://lore.kernel.org/r/20250305045842.4117767-1-Shyam-sundar.S-k@amd.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari fb9f763c1e platform/x86/amd: pmf: Add balanced-performance to hidden choices
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 9a43102daf64dd0d172d8b39836dbc1dba4da1ea
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Fri Feb 28 11:01:55 2025 -0600

    platform/x86/amd: pmf: Add balanced-performance to hidden choices

    Acer's WMI driver uses balanced-performance but AMD-PMF doesn't.
    In case a machine binds with both drivers let amd-pmf use
    balanced-performance as well.

    Fixes: 688834743d67 ("ACPI: platform_profile: Allow multiple handlers")
    Suggested-by: Antheas Kapenekakis <lkml@antheas.dev>
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Tested-by: Antheas Kapenekakis <lkml@antheas.dev>
    Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>
    Acked-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Link: https://patch.msgid.link/20250228170155.2623386-4-superm1@kernel.org
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari 51c2c3eb01 platform/x86/amd: pmf: Add 'quiet' to hidden choices
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 44e94fece5170ed9110564efec592d0e88830a28
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Fri Feb 28 11:01:54 2025 -0600

    platform/x86/amd: pmf: Add 'quiet' to hidden choices

    When amd-pmf and asus-wmi are both bound no low power option shows
    up in sysfs.  Add a hidden choice for amd-pmf to support 'quiet' mode
    to let both bind.

    Fixes: 688834743d67 ("ACPI: platform_profile: Allow multiple handlers")
    Suggested-by: Antheas Kapenekakis <lkml@antheas.dev>
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Tested-by: Antheas Kapenekakis <lkml@antheas.dev>
    Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>
    Acked-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Link: https://patch.msgid.link/20250228170155.2623386-3-superm1@kernel.org
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari 8c07f32d52 platform/x86/amd/pmf: Initialize and clean up `cb_mutex`
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 1be4e29e94a6be77de3bc210820b74f40814f17a
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Thu Feb 27 11:03:06 2025 -0600

    platform/x86/amd/pmf: Initialize and clean up `cb_mutex`

    `cb_mutex` was introduced in commit 9e0894d07072e ("platform/x86/amd/pmf:
    Enable Custom BIOS Inputs for PMF-TA") to prevent conccurrent access for
    BIOS inputs. It however isn't initialized and so on use it may lead to
    a NULL pointer dereference.

    Add code to initialize on probe and clean up on destroy.

    Reported-by: Yijun Shen <Yijun.Shen@dell.com>
    Cc: Richard Gong <richgong@amd.com>
    Fixes: 9e0894d07072e ("platform/x86/amd/pmf: Enable Custom BIOS Inputs for PMF-TA")
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Tested-By: Yijun Shen <Yijun_Shen@Dell.com>
    Acked-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
    Link: https://lore.kernel.org/r/20250227170308.435862-1-superm1@kernel.org
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari e45261c7ab ACPI: platform-profile: Fix CFI violation when accessing sysfs files
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit dd4f730b557ce701a2cd4f604bf1e57667bd8b6e
Author: Nathan Chancellor <nathan@kernel.org>
Date:   Mon Feb 10 21:28:25 2025 -0500

    ACPI: platform-profile: Fix CFI violation when accessing sysfs files

    When an attribute group is created with sysfs_create_group(), the
    ->sysfs_ops() callback is set to kobj_sysfs_ops, which sets the ->show()
    and ->store() callbacks to kobj_attr_show() and kobj_attr_store()
    respectively. These functions use container_of() to get the respective
    callback from the passed attribute, meaning that these callbacks need to
    be of the same type as the callbacks in 'struct kobj_attribute'.

    However, ->show() and ->store() in the platform_profile driver are
    defined for struct device_attribute with the help of DEVICE_ATTR_RO()
    and DEVICE_ATTR_RW(), which results in a CFI violation when accessing
    platform_profile or platform_profile_choices under /sys/firmware/acpi
    because the types do not match:

      CFI failure at kobj_attr_show+0x19/0x30 (target: platform_profile_choices_show+0x0/0x140; expected type: 0x7a69590c)

    There is no functional issue from the type mismatch because the layout
    of 'struct kobj_attribute' and 'struct device_attribute' are the same,
    so the container_of() cast does not break anything aside from CFI.

    Change the type of platform_profile_choices_show() and
    platform_profile_{show,store}() to match the callbacks in
    'struct kobj_attribute' and update the attribute variables to
    match, which resolves the CFI violation.

    Cc: All applicable <stable@vger.kernel.org>
    Fixes: a2ff95e018 ("ACPI: platform: Add platform profile support")
    Reported-by: John Rowley <lkml@johnrowley.me>
    Closes: https://github.com/ClangBuiltLinux/linux/issues/2047
    Tested-by: John Rowley <lkml@johnrowley.me>
    Reviewed-by: Sami Tolvanen <samitolvanen@google.com>
    Signed-off-by: Nathan Chancellor <nathan@kernel.org>
    Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://patch.msgid.link/20250210-acpi-platform_profile-fix-cfi-violation-v3-1-ed9e9901c33a@kernel.org
    [ rjw: Changelog edits ]
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:31 -04:00
David Arcari 28f4eae68b ACPI: platform_profile: Add support for hidden choices
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 778b94d7ac17b5800aa857222911f09cc986b509
Author: Mario Limonciello <mario.limonciello@amd.com>
Date:   Fri Feb 28 11:01:53 2025 -0600

    ACPI: platform_profile: Add support for hidden choices

    When two drivers don't support all the same profiles the legacy interface
    only exports the common profiles.

    This causes problems for cases where one driver uses low-power but another
    uses quiet because the result is that neither is exported to sysfs.

    To allow two drivers to disagree, add support for "hidden choices".
    Hidden choices are platform profiles that a driver supports to be
    compatible with the platform profile of another driver.

    Fixes: 688834743d67 ("ACPI: platform_profile: Allow multiple handlers")
    Reported-by: Antheas Kapenekakis <lkml@antheas.dev>
    Closes: https://lore.kernel.org/platform-driver-x86/e64b771e-3255-42ad-9257-5b8fc6c24ac9@gmx.de/T/#mc068042dd29df36c16c8af92664860fc4763974b
    Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
    Tested-by: Antheas Kapenekakis <lkml@antheas.dev>
    Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>
    Acked-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Link: https://patch.msgid.link/20250228170155.2623386-2-superm1@kernel.org
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari dc049d84dc ACPI: platform_profile: Fix memory leak in profile_class_is_visible()
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit d403120cb9d4787b283ea202b2162f459d18fe9d
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Feb 12 14:30:58 2025 -0500

    ACPI: platform_profile: Fix memory leak in profile_class_is_visible()

    If class_find_device() finds a device, it's reference count is
    incremented.

    Call put_device() to drop this reference before returning.

    Fixes: 77be5cacb2c2 ("ACPI: platform_profile: Create class for ACPI platform profile")
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://patch.msgid.link/20250212193058.32110-1-kuurtb@gmail.com
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari 8ead803dab ACPI: platform_profile: Improve platform_profile_unregister()
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit bb519cf6113473c09d571e555137a36d7e2c8566
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Feb 12 14:03:08 2025 -0500

    ACPI: platform_profile: Improve platform_profile_unregister()

    Drivers usually call this method on error/exit paths and do not check
    for it's return value, which is always 0 anyway, so make it void.

    This is safe to do as currently all drivers use
    devm_platform_profile_register().

    While at it, improve the style and make the function safer by checking
    for IS_ERR_OR_NULL before dereferencing the device pointer.

    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://patch.msgid.link/20250212190308.21209-1-kuurtb@gmail.com
    [ rjw: Minor changelog edits ]
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari 460c9520ae ACPI: platform_profile: Add a prefix to log messages
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 0025d83a3c201d0f1cda1085a3847591af563ec2
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:21 2025 -0500

    ACPI: platform_profile: Add a prefix to log messages

    Declare a pr_fmt prefix.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-20-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari 914c7744d1 ACPI: platform_profile: Add documentation
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit ee7f3e2b4942e3f6d8837780d0d3d5d58de8801a
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:20 2025 -0500

    ACPI: platform_profile: Add documentation

    Add kerneldoc and sysfs class documentation.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-19-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari 74416b223d ACPI: platform_profile: Clean platform_profile_handler
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit c4f7d255be321d6cb0543174509f2b39f7c763b9
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:19 2025 -0500

    ACPI: platform_profile: Clean platform_profile_handler

    Remove parent device *dev from platform_profile_handler, as it's no
    longer accessed directly. Rename class_dev -> dev.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-18-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari 8ae1ea21ec ACPI: platform_profile: Move platform_profile_handler
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 6ef33895503583d0741a0d8faf820ca8143b9cf2
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:18 2025 -0500

    ACPI: platform_profile: Move platform_profile_handler

    platform_profile_handler is now an internal structure. Move it to
    platform_profile.c.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-17-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:22:30 -04:00
David Arcari 0bc9ecab4c platform/x86: thinkpad_acpi: Fix registration of tpacpi platform driver
JIRA: https://issues.redhat.com/browse/RHEL-105420
Conflicts: thinkpad_acpi.c has dytc_profile_available which no
	   longer upstream.

commit b3e127dacad60a384c92baafdc74f1508bf7dd47
Author: Mark Pearson <mpearson-lenovo@squebb.ca>
Date:   Tue Feb 11 12:36:11 2025 -0500

    platform/x86: thinkpad_acpi: Fix registration of tpacpi platform driver

    The recent platform profile changes prevent the tpacpi platform driver
    from registering. This error is seen in the kernel logs, and the
    various tpacpi entries are not created:

    [ 7550.642171] platform thinkpad_acpi: Resources present before probing

    This happens because devm_platform_profile_register() is called before
    tpacpi_pdev probes (thanks to Kurt Borja for identifying the root
    cause).

    For now revert back to the old platform_profile_register to fix the
    issue. This is quick fix and will be re-implemented later as more
    testing is needed for full solution.

    Tested on X1 Carbon G12.

    Fixes: 31658c916fa6 ("platform/x86: thinkpad_acpi: Use devm_platform_profile_register()")
    Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Reviewed-by: Kurt Borja <kuurtb@gmail.com>
    Link: https://lore.kernel.org/r/20250211173620.16522-1-mpearson-lenovo@squebb.ca
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Conflicts:
- drivers/platform/x86/thinkpad_acpi.c: 1 hunk modified

Assisted-by: Patchpal AI
Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:20:15 -04:00
David Arcari 4dfed50f30 ACPI: platform_profile: Remove platform_profile_handler from exported symbols
JIRA: https://issues.redhat.com/browse/RHEL-105420
Conflcts: RHEL is out of date with upstream changes were applied as
	  appropriate.

commit 07f531b395db3cd1776ef0f7191abf4b077fcf21
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:17 2025 -0500

    ACPI: platform_profile: Remove platform_profile_handler from exported symbols

    In order to protect the platform_profile_handler from API consumers,
    allocate it in platform_profile_register() and modify it's signature
    accordingly.

    Remove the platform_profile_handler from all consumer drivers and
    replace them with a pointer to the class device, which is
    now returned from platform_profile_register().

    Replace *pprof with a pointer to the class device in the rest of
    exported symbols.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-16-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:18:49 -04:00
David Arcari 656189a4f4 platform/x86: thinkpad_acpi: Use devm_platform_profile_register()
JIRA: https://issues.redhat.com/browse/RHEL-105420
Conflicts: RHEL is out of date with upstream and thinkpad_acpi.c
	   uses dytc_profile_available.  This reference will be
	   added back by a later commit.

commit 31658c916fa692b7dfceaba6fc6320b81c05b9c2
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:16 2025 -0500

    platform/x86: thinkpad_acpi: Use devm_platform_profile_register()

    Replace platform_profile_register() with it's device managed version.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-15-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:13:35 -04:00
David Arcari ed58761c17 platform/x86: hp-wmi: Use devm_platform_profile_register()
JIRA: https://issues.redhat.com/browse/RHEL-105420
Conflicts: commits were applied out of order that introduced ops structure

commit db0c8eb72bc7ec65be830783461860de5041c71f
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:14 2025 -0500

    platform/x86: hp-wmi: Use devm_platform_profile_register()

    Replace platform_profile_register() with it's device managed version.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-13-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:13:17 -04:00
David Arcari 2e96a5392a platform/x86: ideapad-laptop: Use devm_platform_profile_register()
JIRA: https://issues.redhat.com/browse/RHEL-105420

commit 0d882fae334be3e519290fbb05472b50b698169d
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:13 2025 -0500

    platform/x86: ideapad-laptop: Use devm_platform_profile_register()

    Replace platform_profile_register() with it's device managed version.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-12-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:12:29 -04:00
David Arcari 89f1924b3e platform/surface: surface_platform_profile: Use devm_platform_profile_register()
JIRA: https://issues.redhat.com/browse/RHEL-105420
Conflicts: RHEL was missing upstream fe0e04cf66a1

commit 24c54b19b4958c8bc390d78d1da3413eb0970b8a
Author: Kurt Borja <kuurtb@gmail.com>
Date:   Wed Jan 15 19:27:08 2025 -0500

    platform/surface: surface_platform_profile: Use devm_platform_profile_register()

    Replace platform_profile_register() with it's device managed version.

    Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
    Signed-off-by: Kurt Borja <kuurtb@gmail.com>
    Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
    Link: https://lore.kernel.org/r/20250116002721.75592-7-kuurtb@gmail.com
    Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
    Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

Assisted-by: Patchpal AI
Signed-off-by: David Arcari <darcari@redhat.com>
2025-09-22 14:11:30 -04:00
15 changed files with 442 additions and 221 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

@ -2,17 +2,34 @@
/* 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>
#define to_pprof_handler(d) (container_of(d, struct platform_profile_handler, class_dev))
#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)];
unsigned long hidden_choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
const struct platform_profile_ops *ops;
};
struct aggregate_choices_data {
unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
int count;
};
static const char * const profile_names[] = {
[PLATFORM_PROFILE_LOW_POWER] = "low-power",
[PLATFORM_PROFILE_COOL] = "cool",
@ -62,7 +79,7 @@ static int _store_class_profile(struct device *dev, void *data)
lockdep_assert_held(&profile_lock);
handler = to_pprof_handler(dev);
if (!test_bit(*bit, handler->choices))
if (!test_bit(*bit, handler->choices) && !test_bit(*bit, handler->hidden_choices))
return -EOPNOTSUPP;
return handler->ops->profile_set(dev, *bit);
@ -80,8 +97,8 @@ 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->class_dev.kobj, NULL, "profile");
kobject_uevent(&handler->class_dev.kobj, KOBJ_CHANGE);
sysfs_notify(&handler->dev.kobj, NULL, "profile");
kobject_uevent(&handler->dev.kobj, KOBJ_CHANGE);
return 0;
}
@ -212,61 +229,101 @@ static struct attribute *profile_attrs[] = {
};
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
* @arg: struct aggregate_choices_data
*
* Return: 0 on success, -errno on failure
*/
static int _aggregate_choices(struct device *dev, void *data)
static int _aggregate_choices(struct device *dev, void *arg)
{
unsigned long tmp[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
struct aggregate_choices_data *data = arg;
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);
bitmap_or(tmp, handler->choices, handler->hidden_choices, PLATFORM_PROFILE_LAST);
if (test_bit(PLATFORM_PROFILE_LAST, data->aggregate))
bitmap_copy(data->aggregate, tmp, PLATFORM_PROFILE_LAST);
else
bitmap_and(aggregate, handler->choices, aggregate, PLATFORM_PROFILE_LAST);
bitmap_and(data->aggregate, tmp, data->aggregate, PLATFORM_PROFILE_LAST);
data->count++;
return 0;
}
/**
* _remove_hidden_choices - Remove hidden choices from aggregate data
* @dev: The device
* @arg: struct aggregate_choices_data
*
* Return: 0 on success, -errno on failure
*/
static int _remove_hidden_choices(struct device *dev, void *arg)
{
struct aggregate_choices_data *data = arg;
struct platform_profile_handler *handler;
lockdep_assert_held(&profile_lock);
handler = to_pprof_handler(dev);
bitmap_andnot(data->aggregate, handler->choices,
handler->hidden_choices, PLATFORM_PROFILE_LAST);
return 0;
}
/**
* platform_profile_choices_show - Show the available profile choices for legacy sysfs interface
* @dev: The device
* @kobj: The kobject
* @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,
static ssize_t platform_profile_choices_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
struct aggregate_choices_data data = {
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
.count = 0,
};
int err;
set_bit(PLATFORM_PROFILE_LAST, aggregate);
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
err = class_for_each_device(&platform_profile_class, NULL,
aggregate, _aggregate_choices);
&data, _aggregate_choices);
if (err)
return err;
if (data.count == 1) {
err = class_for_each_device(&platform_profile_class, NULL,
&data, _remove_hidden_choices);
if (err)
return err;
}
}
/* no profile handler registered any more */
if (bitmap_empty(aggregate, PLATFORM_PROFILE_LAST))
if (bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
return -EINVAL;
return _commmon_choices_show(aggregate, buf);
return _commmon_choices_show(data.aggregate, buf);
}
/**
@ -314,14 +371,14 @@ static int _store_and_notify(struct device *dev, void *data)
/**
* platform_profile_show - Show the current profile for legacy sysfs interface
* @dev: The device
* @kobj: The kobject
* @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,
static ssize_t platform_profile_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
@ -343,18 +400,21 @@ static ssize_t platform_profile_show(struct device *dev,
/**
* platform_profile_store - Set the profile for legacy sysfs interface
* @dev: The device
* @kobj: The kobject
* @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,
static ssize_t platform_profile_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
struct aggregate_choices_data data = {
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
.count = 0,
};
int ret;
int i;
@ -362,13 +422,13 @@ static ssize_t platform_profile_store(struct device *dev,
i = sysfs_match_string(profile_names, buf);
if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
return -EINVAL;
set_bit(PLATFORM_PROFILE_LAST, choices);
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
ret = class_for_each_device(&platform_profile_class, NULL,
choices, _aggregate_choices);
&data, _aggregate_choices);
if (ret)
return ret;
if (!test_bit(i, choices))
if (!test_bit(i, data.aggregate))
return -EOPNOTSUPP;
ret = class_for_each_device(&platform_profile_class, NULL, &i,
@ -382,12 +442,12 @@ static ssize_t platform_profile_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RO(platform_profile_choices);
static DEVICE_ATTR_RW(platform_profile);
static struct kobj_attribute attr_platform_profile_choices = __ATTR_RO(platform_profile_choices);
static struct kobj_attribute attr_platform_profile = __ATTR_RW(platform_profile);
static struct attribute *platform_profile_attrs[] = {
&dev_attr_platform_profile_choices.attr,
&dev_attr_platform_profile.attr,
&attr_platform_profile_choices.attr,
&attr_platform_profile.attr,
NULL
};
@ -398,8 +458,14 @@ static int profile_class_registered(struct device *dev, const void *data)
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))
struct device *dev;
dev = class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered);
if (!dev)
return 0;
put_device(dev);
return attr->mode;
}
@ -408,23 +474,35 @@ static const struct attribute_group platform_profile_group = {
.is_visible = profile_class_is_visible,
};
void platform_profile_notify(struct platform_profile_handler *pprof)
/**
* platform_profile_notify - Notify class device and legacy sysfs interface
* @dev: The class device
*/
void platform_profile_notify(struct device *dev)
{
scoped_cond_guard(mutex_intr, return, &profile_lock) {
_notify_class_profile(&pprof->class_dev, NULL);
_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)
{
struct aggregate_choices_data data = {
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
.count = 0,
};
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;
set_bit(PLATFORM_PROFILE_LAST, choices);
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
err = class_for_each_device(&platform_profile_class, NULL,
&profile, _aggregate_profiles);
@ -436,14 +514,14 @@ int platform_profile_cycle(void)
return -EINVAL;
err = class_for_each_device(&platform_profile_class, NULL,
choices, _aggregate_choices);
&data, _aggregate_choices);
if (err)
return err;
/* never iterate into a custom if all drivers supported it */
clear_bit(PLATFORM_PROFILE_CUSTOM, choices);
clear_bit(PLATFORM_PROFILE_CUSTOM, data.aggregate);
next = find_next_bit_wrap(choices,
next = find_next_bit_wrap(data.aggregate,
PLATFORM_PROFILE_LAST,
profile + 1);
@ -460,42 +538,71 @@ int platform_profile_cycle(void)
}
EXPORT_SYMBOL_GPL(platform_profile_cycle);
int platform_profile_register(struct platform_profile_handler *pprof, void *drvdata)
/**
* 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;
/* Sanity check the profile handler */
if (!pprof || !pprof->ops->profile_set || !pprof->ops->profile_get ||
!pprof->ops->probe) {
pr_err("platform_profile: handler is invalid\n");
return -EINVAL;
}
/* Sanity check */
if (WARN_ON_ONCE(!dev || !name || !ops || !ops->profile_get ||
!ops->profile_set || !ops->probe))
return ERR_PTR(-EINVAL);
err = pprof->ops->probe(drvdata, pprof->choices);
struct platform_profile_handler *pprof __free(kfree) = kzalloc(
sizeof(*pprof), GFP_KERNEL);
if (!pprof)
return ERR_PTR(-ENOMEM);
err = ops->probe(drvdata, pprof->choices);
if (err) {
dev_err(pprof->dev, "platform_profile probe failed\n");
return err;
dev_err(dev, "platform_profile probe failed\n");
return ERR_PTR(err);
}
if (bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST)) {
dev_err(pprof->dev, "Failed to register a platform_profile class device with empty choices\n");
return -EINVAL;
dev_err(dev, "Failed to register platform_profile class device with empty choices\n");
return ERR_PTR(-EINVAL);
}
if (ops->hidden_choices) {
err = ops->hidden_choices(drvdata, pprof->hidden_choices);
if (err) {
dev_err(dev, "platform_profile hidden_choices failed\n");
return ERR_PTR(err);
}
}
guard(mutex)(&profile_lock);
/* create class interface for individual handler */
pprof->minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
if (pprof->minor < 0)
return pprof->minor;
minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
if (minor < 0)
return ERR_PTR(minor);
pprof->class_dev.class = &platform_profile_class;
pprof->class_dev.parent = pprof->dev;
dev_set_drvdata(&pprof->class_dev, drvdata);
dev_set_name(&pprof->class_dev, "platform-profile-%d", pprof->minor);
err = device_register(&pprof->class_dev);
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(&pprof->class_dev);
put_device(ppdev);
goto cleanup_ida;
}
@ -505,61 +612,78 @@ int platform_profile_register(struct platform_profile_handler *pprof, void *drvd
if (err)
goto cleanup_cur;
return 0;
return ppdev;
cleanup_cur:
device_unregister(&pprof->class_dev);
device_unregister(ppdev);
cleanup_ida:
ida_free(&platform_profile_ida, pprof->minor);
ida_free(&platform_profile_ida, minor);
return err;
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(platform_profile_register);
int platform_profile_remove(struct platform_profile_handler *pprof)
/**
* platform_profile_remove - Unregisters a platform profile class device
* @dev: Class device
*/
void platform_profile_remove(struct device *dev)
{
int id;
struct platform_profile_handler *pprof;
if (IS_ERR_OR_NULL(dev))
return;
pprof = to_pprof_handler(dev);
guard(mutex)(&profile_lock);
id = pprof->minor;
device_unregister(&pprof->class_dev);
ida_free(&platform_profile_ida, id);
ida_free(&platform_profile_ida, pprof->minor);
device_unregister(&pprof->dev);
sysfs_notify(acpi_kobj, NULL, "platform_profile");
sysfs_update_group(acpi_kobj, &platform_profile_group);
return 0;
}
EXPORT_SYMBOL_GPL(platform_profile_remove);
static void devm_platform_profile_release(struct device *dev, void *res)
{
struct platform_profile_handler **pprof = res;
struct device **ppdev = res;
platform_profile_remove(*pprof);
platform_profile_remove(*ppdev);
}
int devm_platform_profile_register(struct platform_profile_handler *pprof, void *drvdata)
/**
* 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 platform_profile_handler **dr;
int ret;
struct device *ppdev;
struct device **dr;
dr = devres_alloc(devm_platform_profile_release, sizeof(*dr), GFP_KERNEL);
if (!dr)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
ret = platform_profile_register(pprof, drvdata);
if (ret) {
ppdev = platform_profile_register(dev, name, drvdata, ops);
if (IS_ERR(ppdev)) {
devres_free(dr);
return ret;
return ppdev;
}
*dr = pprof;
devres_add(pprof->dev, dr);
*dr = ppdev;
devres_add(dev, dr);
return 0;
return ppdev;
}
EXPORT_SYMBOL_GPL(devm_platform_profile_register);
@ -567,6 +691,9 @@ static int __init platform_profile_init(void)
{
int err;
if (acpi_disabled)
return -EOPNOTSUPP;
err = class_register(&platform_profile_class);
if (err)
return err;

View File

@ -29,7 +29,7 @@ struct ssam_tmp_profile_info {
struct ssam_tmp_profile_device {
struct ssam_device *sdev;
struct platform_profile_handler handler;
struct device *ppdev;
};
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {
@ -168,25 +168,15 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
tpd->sdev = sdev;
ssam_device_set_drvdata(sdev, tpd);
tpd->handler.name = "Surface Platform Profile";
tpd->handler.dev = &sdev->dev;
tpd->handler.ops = &ssam_platform_profile_ops;
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);
platform_profile_register(&tpd->handler, tpd);
return 0;
}
tpd->ppdev = devm_platform_profile_register(&sdev->dev, "Surface Platform Profile",
tpd, &ssam_platform_profile_ops);
static void surface_platform_profile_remove(struct ssam_device *sdev)
{
struct ssam_platform_profile_device *tpd;
tpd = ssam_device_get_drvdata(sdev);
platform_profile_remove(&tpd->handler);
return PTR_ERR_OR_ZERO(tpd->ppdev);
}
static const struct ssam_device_id ssam_platform_profile_match[] = {
@ -197,7 +187,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

@ -220,7 +220,7 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
if (!info)
return;
schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
schedule_delayed_work(&dev->heart_beat, secs_to_jiffies(dev->hb_interval));
kfree(info);
}

View File

@ -120,9 +120,9 @@ static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx,
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL);
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual,

View File

@ -81,10 +81,10 @@ static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx,
amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],
NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],
NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL);
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
apmf_update_fan_idx(dev,

View File

@ -176,6 +176,20 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
}
/**
* fixp_q88_fromint: Convert integer to Q8.8
* @val: input value
*
* Converts an integer into binary fixed point format where 8 bits
* are used for integer and 8 bits are used for the decimal.
*
* Return: unsigned integer converted to Q8.8 format
*/
u32 fixp_q88_fromint(u32 val)
{
return val << 8;
}
int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
{
int rc;
@ -266,7 +280,7 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
}
dev->buf = kzalloc(dev->mtable_size, GFP_KERNEL);
dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
if (!dev->buf)
return -ENOMEM;
}
@ -452,6 +466,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
mutex_init(&dev->lock);
mutex_init(&dev->update_mutex);
mutex_init(&dev->cb_mutex);
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
@ -477,7 +492,7 @@ static void amd_pmf_remove(struct platform_device *pdev)
amd_pmf_dbgfs_unregister(dev);
mutex_destroy(&dev->lock);
mutex_destroy(&dev->update_mutex);
kfree(dev->buf);
mutex_destroy(&dev->cb_mutex);
}
static const struct attribute_group *amd_pmf_driver_groups[] = {

View File

@ -106,9 +106,12 @@ struct cookie_header {
#define PMF_TA_IF_VERSION_MAJOR 1
#define TA_PMF_ACTION_MAX 32
#define TA_PMF_UNDO_MAX 8
#define TA_OUTPUT_RESERVED_MEM 906
#define TA_OUTPUT_RESERVED_MEM 922
#define MAX_OPERATION_PARAMS 4
#define TA_ERROR_CRYPTO_INVALID_PARAM 0x20002
#define TA_ERROR_CRYPTO_BIN_TOO_LARGE 0x2000d
#define PMF_IF_V1 1
#define PMF_IF_V2 2
@ -338,7 +341,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;
@ -774,6 +777,7 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev);
int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
u32 fixp_q88_fromint(u32 val);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);

View File

@ -219,12 +219,14 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_
switch (dev->current_profile) {
case PLATFORM_PROFILE_PERFORMANCE:
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
val = TA_BEST_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
val = TA_BETTER_PERFORMANCE;
break;
case PLATFORM_PROFILE_LOW_POWER:
case PLATFORM_PROFILE_QUIET:
val = TA_BEST_BATTERY;
break;
default:

View File

@ -198,9 +198,11 @@ static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
apts_config_store.val[idx].stt_min_limit, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL);
fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu),
NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL);
fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2),
NULL);
}
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
@ -217,9 +219,11 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
config_store.prop[src][idx].stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]),
NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]),
NULL);
} else if (op == SLIDER_OP_GET) {
amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
@ -297,12 +301,14 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
switch (pmf->current_profile) {
case PLATFORM_PROFILE_PERFORMANCE:
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
mode = POWER_MODE_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
mode = POWER_MODE_BALANCED_POWER;
break;
case PLATFORM_PROFILE_LOW_POWER:
case PLATFORM_PROFILE_QUIET:
mode = POWER_MODE_POWER_SAVER;
break;
default:
@ -387,6 +393,14 @@ static int amd_pmf_profile_set(struct device *dev,
return 0;
}
static int amd_pmf_hidden_choices(void *drvdata, unsigned long *choices)
{
set_bit(PLATFORM_PROFILE_QUIET, choices);
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
return 0;
}
static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices)
{
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
@ -398,14 +412,13 @@ static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices)
static const struct platform_profile_ops amd_pmf_profile_ops = {
.probe = amd_pmf_profile_probe,
.hidden_choices = amd_pmf_hidden_choices,
.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)) {
@ -420,15 +433,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
amd_pmf_set_sps_power_limits(dev);
}
dev->pprof.name = "amd-pmf";
dev->pprof.dev = dev->dev;
dev->pprof.ops = &amd_pmf_profile_ops;
/* Create platform_profile structure and register */
err = devm_platform_profile_register(&dev->pprof, dev);
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;
return PTR_ERR_OR_ZERO(dev->ppdev);
}

View File

@ -27,8 +27,11 @@ module_param(pb_side_load, bool, 0444);
MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures");
#endif
static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d,
0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43);
static const uuid_t amd_pmf_ta_uuid[] = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8, 0x8a,
0xcc, 0x2b, 0x2b, 0x60, 0xd6),
UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5,
0x29, 0xb1, 0x3d, 0x85, 0x43),
};
static const char *amd_pmf_uevent_as_str(unsigned int state)
{
@ -120,7 +123,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_SKINTEMP_APU:
if (dev->prev_data->stt_skintemp_apu != val) {
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, val, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
fixp_q88_fromint(val), NULL);
dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val);
dev->prev_data->stt_skintemp_apu = val;
}
@ -128,7 +132,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_SKINTEMP_HS2:
if (dev->prev_data->stt_skintemp_hs2 != val) {
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, val, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
fixp_q88_fromint(val), NULL);
dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val);
dev->prev_data->stt_skintemp_hs2 = val;
}
@ -321,14 +326,19 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
*/
schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms * 3));
} else {
dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res);
dev_dbg(dev->dev, "ta invoke cmd init failed err: %x\n", res);
dev->smart_pc_enabled = false;
return -EIO;
return res;
}
return 0;
}
static inline bool amd_pmf_pb_valid(struct amd_pmf_dev *dev)
{
return memchr_inv(dev->policy_buf, 0xff, dev->policy_sz);
}
#ifdef CONFIG_AMD_PMF_DEBUG
static void amd_pmf_hex_dump_pb(struct amd_pmf_dev *dev)
{
@ -348,14 +358,22 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
return -EINVAL;
/* re-alloc to the new buffer length of the policy binary */
new_policy_buf = memdup_user(buf, length);
if (IS_ERR(new_policy_buf))
return PTR_ERR(new_policy_buf);
new_policy_buf = devm_kzalloc(dev->dev, length, GFP_KERNEL);
if (!new_policy_buf)
return -ENOMEM;
kfree(dev->policy_buf);
if (copy_from_user(new_policy_buf, buf, length)) {
devm_kfree(dev->dev, new_policy_buf);
return -EFAULT;
}
devm_kfree(dev->dev, dev->policy_buf);
dev->policy_buf = new_policy_buf;
dev->policy_sz = length;
if (!amd_pmf_pb_valid(dev))
return -EINVAL;
amd_pmf_hex_dump_pb(dev);
ret = amd_pmf_start_policy_engine(dev);
if (ret < 0)
@ -390,24 +408,24 @@ static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const voi
return ver->impl_id == TEE_IMPL_ID_AMDTEE;
}
static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id)
static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id, const uuid_t *uuid)
{
struct tee_ioctl_open_session_arg sess_arg = {};
int rc;
export_uuid(sess_arg.uuid, &amd_pmf_ta_uuid);
export_uuid(sess_arg.uuid, uuid);
sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
sess_arg.num_params = 0;
rc = tee_client_open_session(ctx, &sess_arg, NULL);
if (rc < 0 || sess_arg.ret != 0) {
pr_err("Failed to open TEE session err:%#x, rc:%d\n", sess_arg.ret, rc);
return rc;
return rc ?: -EINVAL;
}
*id = sess_arg.session;
return rc;
return 0;
}
static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
@ -434,7 +452,7 @@ static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
return 0;
}
static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid)
{
u32 size;
int ret;
@ -442,10 +460,12 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
dev->tee_ctx = tee_client_open_context(NULL, amd_pmf_amdtee_ta_match, NULL, NULL);
if (IS_ERR(dev->tee_ctx)) {
dev_err(dev->dev, "Failed to open TEE context\n");
return PTR_ERR(dev->tee_ctx);
ret = PTR_ERR(dev->tee_ctx);
dev->tee_ctx = NULL;
return ret;
}
ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id);
ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id, uuid);
if (ret) {
dev_err(dev->dev, "Failed to open TA session (%d)\n", ret);
ret = -EINVAL;
@ -482,14 +502,18 @@ out_ctx:
static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev)
{
if (!dev->tee_ctx)
return;
tee_shm_free(dev->fw_shm_pool);
tee_client_close_session(dev->tee_ctx, dev->session_id);
tee_client_close_context(dev->tee_ctx);
dev->tee_ctx = NULL;
}
int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
{
int ret;
bool status;
int ret, i;
ret = apmf_check_smart_pc(dev);
if (ret) {
@ -502,52 +526,61 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
return -ENODEV;
}
ret = amd_pmf_tee_init(dev);
if (ret)
return ret;
INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd);
ret = amd_pmf_set_dram_addr(dev, true);
if (ret)
goto error;
return ret;
dev->policy_base = devm_ioremap_resource(dev->dev, dev->res);
if (IS_ERR(dev->policy_base)) {
ret = PTR_ERR(dev->policy_base);
goto error;
}
if (IS_ERR(dev->policy_base))
return PTR_ERR(dev->policy_base);
dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL);
if (!dev->policy_buf) {
ret = -ENOMEM;
goto error;
}
dev->policy_buf = devm_kzalloc(dev->dev, dev->policy_sz, GFP_KERNEL);
if (!dev->policy_buf)
return -ENOMEM;
memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz);
amd_pmf_hex_dump_pb(dev);
dev->prev_data = kzalloc(sizeof(*dev->prev_data), GFP_KERNEL);
if (!dev->prev_data) {
ret = -ENOMEM;
goto error;
if (!amd_pmf_pb_valid(dev)) {
dev_info(dev->dev, "No Smart PC policy present\n");
return -EINVAL;
}
ret = amd_pmf_start_policy_engine(dev);
if (ret)
goto error;
amd_pmf_hex_dump_pb(dev);
dev->prev_data = devm_kzalloc(dev->dev, sizeof(*dev->prev_data), GFP_KERNEL);
if (!dev->prev_data)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(amd_pmf_ta_uuid); i++) {
ret = amd_pmf_tee_init(dev, &amd_pmf_ta_uuid[i]);
if (ret)
return ret;
ret = amd_pmf_start_policy_engine(dev);
dev_dbg(dev->dev, "start policy engine ret: %d\n", ret);
status = ret == TA_PMF_TYPE_SUCCESS;
if (status)
break;
amd_pmf_tee_deinit(dev);
}
if (!status && !pb_side_load) {
ret = -EINVAL;
goto err;
}
if (pb_side_load)
amd_pmf_open_pb(dev, dev->dbgfs_dir);
ret = amd_pmf_register_input_device(dev);
if (ret)
goto error;
goto err;
return 0;
error:
err:
amd_pmf_deinit_smart_pc(dev);
return ret;
@ -562,11 +595,5 @@ void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev)
amd_pmf_remove_pb(dev);
cancel_delayed_work_sync(&dev->pb_work);
kfree(dev->prev_data);
dev->prev_data = NULL;
kfree(dev->policy_buf);
dev->policy_buf = NULL;
kfree(dev->buf);
dev->buf = NULL;
amd_pmf_tee_deinit(dev);
}

View File

@ -166,7 +166,7 @@ static const struct key_entry hp_wmi_keymap[] = {
static struct input_dev *hp_wmi_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 bool platform_profile_support;
static struct rfkill *wifi_rfkill;
@ -957,6 +957,7 @@ static const struct platform_profile_ops hp_wmi_platform_profile_ops = {
static int thermal_profile_setup(struct platform_device *device)
{
const struct platform_profile_ops *ops;
int err, tp;
tp = thermal_profile_get();
@ -971,13 +972,12 @@ static int thermal_profile_setup(struct platform_device *device)
if (err)
return err;
platform_profile_handler.name = "hp-wmi";
platform_profile_handler.dev = &device->dev;
platform_profile_handler.ops = &hp_wmi_platform_profile_ops;
ops = &hp_wmi_platform_profile_ops;
err = platform_profile_register(&platform_profile_handler, NULL);
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);
platform_profile_support = true;
@ -1022,9 +1022,6 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
rfkill_destroy(wwan_rfkill);
}
if (platform_profile_support)
platform_profile_remove(&platform_profile_handler);
return 0;
}

View File

@ -109,7 +109,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;
};
@ -872,7 +872,7 @@ static void dytc_profile_refresh(struct ideapad_private *priv)
if (profile != priv->dytc->current_profile) {
priv->dytc->current_profile = profile;
platform_profile_notify(&priv->dytc->pprof);
platform_profile_notify(priv->dytc->ppdev);
}
}
@ -909,15 +909,16 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv)
mutex_init(&priv->dytc->mutex);
priv->dytc->pprof.name = "ideapad-laptop";
priv->dytc->pprof.dev = &priv->platform_device->dev;
priv->dytc->priv = priv;
priv->dytc->pprof.ops = &dytc_profile_ops;
/* Create platform_profile structure and register */
err = platform_profile_register(&priv->dytc->pprof, &priv->dytc);
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);
@ -937,7 +938,6 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
if (!priv->dytc)
return;
platform_profile_remove(&priv->dytc->pprof);
mutex_destroy(&priv->dytc->mutex);
kfree(priv->dytc);

View File

@ -932,6 +932,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);
@ -10296,11 +10297,6 @@ static const struct platform_profile_ops dytc_profile_ops = {
.profile_set = dytc_profile_set,
};
static struct platform_profile_handler dytc_profile = {
.name = "thinkpad-acpi",
.ops = &dytc_profile_ops,
};
static void dytc_profile_refresh(void)
{
enum platform_profile_option profile;
@ -10320,7 +10316,7 @@ static void dytc_profile_refresh(void)
convert_dytc_to_profile(perfmode, &profile);
if (profile != dytc_current_profile) {
dytc_current_profile = profile;
platform_profile_notify(&dytc_profile);
platform_profile_notify(tpacpi_pprof);
}
}
@ -10360,15 +10356,15 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS))
dytc_mmc_get_available = true;
}
dytc_profile.dev = &tpacpi_pdev->dev;
/* Create platform_profile structure and register */
err = platform_profile_register(&dytc_profile, NULL);
tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile",
NULL, &dytc_profile_ops);
/*
* If for some reason platform_profiles aren't enabled
* don't quit terminally.
*/
if (err)
return 0;
if (IS_ERR(tpacpi_pprof))
return -ENODEV;
dytc_profile_available = true;
/* Ensure initial values are correct */
@ -10379,10 +10375,10 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
static void dytc_profile_exit(void)
{
if (dytc_profile_available) {
if (!IS_ERR_OR_NULL(tpacpi_pprof))
platform_profile_remove(tpacpi_pprof);
if (dytc_profile_available)
dytc_profile_available = false;
platform_profile_remove(&dytc_profile);
}
}
static struct ibm_struct dytc_profile_driver_data = {

View File

@ -28,27 +28,33 @@ enum platform_profile_option {
PLATFORM_PROFILE_LAST, /*must always be last */
};
struct platform_profile_handler;
/**
* 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.
* @hidden_choices: Callback to setup choices that are not visible to the user
* but can be set by the driver.
* @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 (*hidden_choices)(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);
};
struct platform_profile_handler {
const char *name;
struct device *dev;
struct device class_dev;
int minor;
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
const struct platform_profile_ops *ops;
};
int platform_profile_register(struct platform_profile_handler *pprof, void *drvdata);
int platform_profile_remove(struct platform_profile_handler *pprof);
int devm_platform_profile_register(struct platform_profile_handler *pprof, void *drvdata);
struct device *platform_profile_register(struct device *dev, const char *name,
void *drvdata,
const struct platform_profile_ops *ops);
void 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(struct platform_profile_handler *pprof);
void platform_profile_notify(struct device *dev);
#endif /*_PLATFORM_PROFILE_H_*/