mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
platform-drivers-x86 for v7.2-1
Highlights:
- amd/hfi: Add support for dynamic ranking tables (version 3)
- amd/pmc:
- Add PMC driver support for AMD 1Ah M80H SoC
- Delay suspend for some Lenovo Laptops to avoid keyboard and lid
switch problems after s2idle
- arm64: qcom-hamoa-ec: Add Hamoa/Purwa/Glymur EC driver
- asus-armoury: add support for G614PR, GA402NJ, GA403UM, and FX608JPR
- asus-wmi: add keystone dongle support
- dell-dw5826e: Add reset driver for DW5826e
- dell-laptop: Fix rollback path
- hp-wmi:
- Add support for Omen 16-ap0xxx (board ID 8D26) and board ID 8B2F
- intel-hid:
- Add HP ProBook x360 440 G1 5 button array support
- Prevent racing ACPI notify handlers
- intel/pmc:
- Add Nova Lake support
- Rate-limit LTR scale-factor warning
- intel-uncore-freq:
- Expose instance ID in the sysfs
- Fix current_freq_khz after CPU hotplug
- intel/vsec: Restore BAR fallback for header walk
- ISST: Restore SST-PP control to all domains
- lenovo-wmi-*:
- Add more CPU tunable attributes
- Add GPU tunable attributes
- Add WMI battery charge limiting
- oxpec: add support for OneXPlayer Super X
- sel3350-platform: Retain LED state on load and unload
- surface: SAM: Add support for Surface Pro 12in
- uniwill-laptop: Add support for battery charge modes
- tools/power/x86/intel-speed-select: Harden daemon pidfile open
- Major refactoring efforts:
- ACPI driver to platform driver conversion
- Converting drivers to use the improved WMI API
- Miscellaneous cleanups / refactoring / improvements
The following is an automated shortlog grouped by driver:
ACPI:
- provide acpi_bus_find_device_by_name()
alienware-wmi-base:
- Transition to new WMI API
amd: hfi:
- Support for ranking table versions
amd/hsmp:
- Clamp ioctl/send_message indices (Spectre v1)
amd/pmc:
- Add delay_suspend module parameter
- Add PMC driver support for AMD 1Ah M80H SoC
- Check for intermediate wakeup in function
- Delay suspend for some Lenovo Laptops
- Don't log during intermediate wakeups
- Use per-SoC cpu_info struct for SMU mailbox and IP info
apple-gmux:
- Drop unused assignment of pnp_device_id driver data
arm64:
- Add driver for EC found on Qualcomm reference devices
arm64: qcom-hamoa-ec:
- Fix indentation in comment tables
arm64 Use named initializers for struct i2c_device_id:
- arm64 Use named initializers for struct i2c_device_id
asus-armoury:
- add support for FX608JPR
- add support for G614PR
- add support for GA402NJ
- add support for GA403UM
asus-wmi:
- add keystone dongle support
barco-p50-gpio:
- attach software node to its target GPIO device
classmate-laptop:
- Address memory leaks on driver removal
- Convert accel driver to a platform one
- Convert ipml driver to a platform one
- Convert keys driver to a platform one
- Convert tablet driver to a platform one
- Convert v4 accel driver to a platform one
- Pass struct device pointer to helpers
- Register ACPI notify handlers directly
- Rename two helper functions
- Unify probe rollback and remove code
dell-ddv:
- Use new buffer-based WMI API
dell-descriptor:
- Use new buffer-based WMI API
dell-dw5826e:
- Add reset driver for DW5826e
dell-laptop:
- add Inspiron N5110 to touchpad LED quirk table
- fix missing cleanups in init error path
dell-privacy:
- correct CONFIG_DELL_WMI_PRIVACY macro name in comments
- Use new buffer-based WMI API
dell_rbu:
- use strscpy in image_type_write
dell-smbios-wmi:
- Use new buffer-based WMI API
dell-wmi-base:
- Use new buffer-based WMI API
docs:
- fix typo in uniwill-laptop.rst
hp-wmi:
- Add support for Omen 16-ap0xxx (8D26)
- Add support for Omen 16-ap0xxx (8E35)
- Add thermal support for board 8B2F
hwmon:
- (dell-smm) Use new buffer-based WMI API
intel-hid:
- Add HP ProBook x360 440 G1 to button_array_table
- Protect ACPI notify handler against recursion
intel/pmc:
- Add ACPI PWRM telemetry driver for Nova Lake S
- Add Nova Lake support to intel_pmc_core driver
- Add NVL PCI IDs for SSRAM telemetry discovery
- Add PMC SSRAM Kconfig description
- Add support for variable DMU offsets
- Enable Pkgc blocking residency counter
- Enable PkgC LTR blocking counter
- rate-limit LTR scale-factor warning
- Retrieve PMC info only for available PMCs
intel/pmc/ssram:
- Add ACPI discovery scaffolding
- Add PCI platform data
- Make PMT registration optional
- Refactor DEVID/PWRMBASE extraction into helper
- Rename probe and PCI ID table for consistency
- Switch to static array with per-index probe state
intel/pmc:
- Use __free() in pmc_core_punit_pmt_init()
- Use PCI DID for PMC SSRAM device discovery
intel/pmt:
- Add pre/post decode hooks around header parsing
- Cache the telemetry discovery header
intel/pmt/crashlog:
- Split init into pre-decode
intel/pmt:
- Pass discovery index instead of resource
intel/pmt/telemetry:
- Move overlap check to post-decode hook
intel/pmt:
- Unify header fetch and add ACPI source
intel/tpmi:
- convert mutex in mem_write() to guard
- use cleanup helpers in mem_write()
intel-uncore-freq:
- Expose instance ID in the sysfs
- Fix current_freq_khz after CPU hotplug
- Rename instance_id
intel/vsec:
- allocate res with intel_vsec_dev
- Restore BAR fallback for header walk
ISST:
- Restore SST-PP control to all domains
lenovo-wmi-capdata:
- Add debugfs file for dumping capdata
lenovo-wmi-helpers:
- Add helper for creating per-device debugfs dir
lenovo-wmi-other:
- Add force_load_psy_ext module parameter
- Add GPU tunable attributes
- Add missing CPU tunable attributes
- Add WMI battery charge limiting
- Rename LWMI_OM_FW_ATTR_BASE_PATH
meraki-mx100:
- use real software node references
modpost:
- Handle malformed WMI GUID strings
Move delayed work on system_dfl_wq:
- Move delayed work on system_dfl_wq
msi-ec:
- Add support for MSI Pulse GL66 12th Gen
oxpec:
- add support for OneXPlayer Super X
panasonic-laptop:
- simplify allocation of sinf
pcengines-apuv2:
- reduce indiraction in swnode assignment
pmc_atom:
- Use named initializer for pci_device_id array
sel3350-platform:
- Retain LED state on load and unload
sonypi:
- use strscpy() in sonypi_acpi_probe
surface: SAM:
- Add support for Surface Pro 12in
thinkpad_acpi:
- Add debugfs entry to display HWDD raw
- Remove unneeded goto
tools/power/x86/intel-speed-select:
- Harden daemon pidfile open
uniwill-laptop:
- Add support for battery charge modes
- Enable battery charge modes on supported devices
- Mark EC_ADDR_OEM_4 as volatile
- Rework FN lock/super key suspend handling
use u8 * for raw byte buffers:
- use u8 * for raw byte buffers
wmi:
- Make sysfs attributes const
- Make wmi_bus_class const
x86-android-tablets:
- enable fwnode matching of GPIO chips
- Use named initializers for struct i2c_device_id
x86/platform/olpc: xo15:
- Convert ACPI driver to a platform one
- Drop wakeup source on driver removal
xo15-ebook:
- Convert ACPI driver to a platform one
- Fix formatting of labels
- Fix wakeup source and GPE handling
- Register ACPI notify handler directly
- Use devres-based resource management
Merges:
- Merge branch 'fixes' into for-next
- Merge branch 'fixes' of into for-next
- Merge branch 'intel-sst' of https://github.com/spandruvada/linux-kernel into review-ilpo-next
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCajl8LgAKCRBZrE9hU+XO
MZ47AP0Z0IoiT8WEcXL4wuTQyR6ruXWClYk82wiroAFEko8Z5AD/WYNWCBXqjZk+
FlJbtleRLA/ADmgT3Mj+biHNTIqyBA0=
=Q3d+
-----END PGP SIGNATURE-----
Merge tag 'platform-drivers-x86-v7.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86
Pull x86 platform driver updates from Ilpo Järvinen:
- amd/hfi: Add support for dynamic ranking tables (version 3)
- amd/pmc:
- Add PMC driver support for AMD 1Ah M80H SoC
- Delay suspend for some Lenovo Laptops to avoid keyboard and lid
switch problems after s2idle
- arm64: qcom-hamoa-ec: Add Hamoa/Purwa/Glymur EC driver
- asus-armoury: add support for G614PR, GA402NJ, GA403UM, and FX608JPR
- asus-wmi: add keystone dongle support
- dell-dw5826e: Add reset driver for DW5826e
- dell-laptop: Fix rollback path
- hp-wmi:
- Add support for Omen 16-ap0xxx (board ID 8D26) and board ID 8B2F
- intel-hid:
- Add HP ProBook x360 440 G1 5 button array support
- Prevent racing ACPI notify handlers
- intel/pmc:
- Add Nova Lake support
- Rate-limit LTR scale-factor warning
- intel-uncore-freq:
- Expose instance ID in the sysfs
- Fix current_freq_khz after CPU hotplug
- intel/vsec: Restore BAR fallback for header walk
- ISST: Restore SST-PP control to all domains
- lenovo-wmi-*:
- Add more CPU tunable attributes
- Add GPU tunable attributes
- Add WMI battery charge limiting
- oxpec: add support for OneXPlayer Super X
- sel3350-platform: Retain LED state on load and unload
- surface: SAM: Add support for Surface Pro 12in
- uniwill-laptop: Add support for battery charge modes
- tools/power/x86/intel-speed-select: Harden daemon pidfile open
- Major refactoring efforts:
- ACPI driver to platform driver conversion
- Converting drivers to use the improved WMI API
- Miscellaneous cleanups / refactoring / improvements
* tag 'platform-drivers-x86-v7.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (115 commits)
platform/x86/intel/pmc: Add NVL PCI IDs for SSRAM telemetry discovery
platform/x86/intel/pmc/ssram: Make PMT registration optional
platform/x86/intel/pmc/ssram: Add ACPI discovery scaffolding
platform/x86/intel/pmc/ssram: Switch to static array with per-index probe state
platform/x86/intel/pmc/ssram: Refactor DEVID/PWRMBASE extraction into helper
platform/x86/intel/pmc/ssram: Add PCI platform data
platform/x86/intel/pmc/ssram: Rename probe and PCI ID table for consistency
platform/x86/intel/pmc: Add ACPI PWRM telemetry driver for Nova Lake S
platform/x86/intel/pmc: Add PMC SSRAM Kconfig description
platform/x86/intel/pmt: Unify header fetch and add ACPI source
platform/x86/intel/pmt: Cache the telemetry discovery header
platform/x86/intel/pmt: Pass discovery index instead of resource
platform/x86/intel/pmt/telemetry: Move overlap check to post-decode hook
platform/x86/intel/pmt/crashlog: Split init into pre-decode
platform/x86/intel/pmt: Add pre/post decode hooks around header parsing
modpost: Handle malformed WMI GUID strings
platform/wmi: Make sysfs attributes const
platform/wmi: Make wmi_bus_class const
hwmon: (dell-smm) Use new buffer-based WMI API
platform/x86: dell-ddv: Use new buffer-based WMI API
...
This commit is contained in:
commit
f31c00c377
|
|
@ -0,0 +1,9 @@
|
|||
What: /sys/bus/platform/devices/<PALC0001 device>/wwan_reset
|
||||
Date: April 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Jackbb Wu <jackbb.wu@compal.com>
|
||||
Description:
|
||||
Writing to this file triggers a Platform Level Device Reset
|
||||
(PLDR) of the Dell DW5826e WWAN module via an ACPI _DSM
|
||||
method. This can be used to recover the modem when it is in
|
||||
a frozen state and unable to respond to USB commands.
|
||||
|
|
@ -58,6 +58,15 @@ Description:
|
|||
* 1 - overboost,
|
||||
* 2 - silent
|
||||
|
||||
What: /sys/devices/platform/<platform>/keystone
|
||||
Date: Jun 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: "Dariusz Figzał" <dariuszfigzal@gmail.com>
|
||||
Description:
|
||||
Reports the Keystone dongle insert state (read-only):
|
||||
* 0 - not inserted
|
||||
* 1 - inserted
|
||||
|
||||
What: /sys/devices/platform/<platform>/gpu_mux_mode
|
||||
Date: Aug 2022
|
||||
KernelVersion: 6.1
|
||||
|
|
|
|||
|
|
@ -46,11 +46,20 @@ Battery Charging Control
|
|||
.. warning:: Some devices do not properly implement the charging threshold interface. Forcing
|
||||
the driver to enable access to said interface on such devices might damage the
|
||||
battery [1]_. Because of this the driver will not enable said feature even when
|
||||
using the ``force`` module parameter.
|
||||
using the ``force`` module parameter. The charging profile interface will be
|
||||
available instead.
|
||||
|
||||
The ``uniwill-laptop`` driver supports controlling the battery charge limit. This happens over
|
||||
the standard ``charge_control_end_threshold`` power supply sysfs attribute. All values
|
||||
between 1 and 100 percent are supported.
|
||||
The ``uniwill-laptop`` driver supports controlling the battery charge limit. This either happens
|
||||
over the standard ``charge_control_end_threshold`` or ``charge_types`` power supply sysfs attribute,
|
||||
depending on the device. When using the ``charge_control_end_threshold`` sysfs attribute, all values
|
||||
between 1 and 100 percent are supported. When using the ``charge_types`` sysfs attribute, the driver
|
||||
supports switching between the ``Standard``, ``Trickle`` and ``Long Life`` profiles.
|
||||
|
||||
Keep in mind that when using the ``charge_types`` sysfs attribute, the EC firmware will hide the
|
||||
true charging status of the battery from the operating system, potentially misleading users into
|
||||
thinking that the charging profile does not work. Checking the ``current_now`` sysfs attribute
|
||||
tells you the true charging status of the battery even when using the ``charge_types`` sysfs
|
||||
attribute (0 means that the battery is currently not charging).
|
||||
|
||||
Additionally the driver signals the presence of battery charging issues through the standard
|
||||
``health`` power supply sysfs attribute.
|
||||
|
|
|
|||
|
|
@ -88,8 +88,15 @@ and "fabric_cluster_id" in the directory.
|
|||
|
||||
Attributes in each directory:
|
||||
|
||||
``instance_id``
|
||||
This attribute is used to get die indices in userspace mapped MMIO
|
||||
blocks. Indices are local to a single TPMI partition. Needed for direct
|
||||
TPMI register access.
|
||||
|
||||
``domain_id``
|
||||
This attribute is used to get the power domain id of this instance.
|
||||
Indices are unique in all TPMI partitions on a given CPU package. Can be
|
||||
used to map compute dies to corresponding CPUs.
|
||||
|
||||
``die_id``
|
||||
This attribute is used to get the Linux die id of this instance.
|
||||
|
|
|
|||
|
|
@ -68,9 +68,28 @@ Each attribute has the following properties:
|
|||
- type
|
||||
|
||||
The following firmware-attributes are implemented:
|
||||
- cpu_temp: CPU Thermal Load Limit
|
||||
- dgpu_boost_clk: Dedicated GPU Boost Clock
|
||||
- dgpu_didvid: Dedicated GPU Device Identifier and Vendor Identifier
|
||||
- dgpu_enable: Dedicated GPU Enabled Status
|
||||
- gpu_mode: GPU Mode by Power Limit
|
||||
- gpu_nv_ac_offset: Nvidia GPU AC Total Processing Power Baseline Offset
|
||||
- gpu_nv_bpl: Nvidia GPU Base Power Limit
|
||||
- gpu_nv_cpu_boost: Nvidia GPU to CPU Dynamic Boost Limit
|
||||
- gpu_nv_ctgp: Nvidia GPU Configurable Total Graphics Power
|
||||
- gpu_nv_ppab: Nvidia GPU Power Performance Aware Boost Limit
|
||||
- gpu_temp: GPU Thermal Load Limit
|
||||
- ppt_cpu_cl: CPU Cross Loading Power Limit
|
||||
- ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
|
||||
- ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
|
||||
- ppt_pl1_spl_cl: Platform Profile Tracking Cross Loading Sustained Power Limit
|
||||
- ppt_pl1_tau: Exceed Duration for Platform Profile Tracking Sustained Power Limit
|
||||
- ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
|
||||
- ppt_pl2_sppt_cl: Platform Profile Tracking Cross Loading Slow Package Tracking
|
||||
- ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
|
||||
- ppt_pl3_fppt_cl: Platform Profile Tracking Cross Loading Fast Package Power Tracking
|
||||
- ppt_pl4_ipl: Platform Profile Tracking Instantaneous Power Limit
|
||||
- ppt_pl4_ipl_cl: Platform Profile Tracking Cross Loading Instantaneous Power Limit
|
||||
|
||||
LENOVO_FAN_TEST_DATA
|
||||
-------------------------
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ Indexed IO
|
|||
|
||||
Indexed IO with IO ports with a granularity of a single byte can be performed using the ``RIOP``
|
||||
(read) and ``WIOP`` (write) ACPI control methods. Those ACPI methods are unused because they
|
||||
provide no benifit when compared to the native IO port access functions provided by the kernel.
|
||||
provide no benefit when compared to the native IO port access functions provided by the kernel.
|
||||
|
||||
Special thanks go to github user `pobrn` which developed the
|
||||
`qc71_laptop <https://github.com/pobrn/qc71_laptop>`_ driver on which this driver is partly based.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ to matching WMI devices using a struct wmi_device_id table:
|
|||
::
|
||||
|
||||
static const struct wmi_device_id foo_id_table[] = {
|
||||
/* Only use uppercase letters! */
|
||||
/* Using only uppercase letters is recommended */
|
||||
{ "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
|
||||
{ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22163,6 +22163,14 @@ F: Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml
|
|||
F: drivers/misc/fastrpc.c
|
||||
F: include/uapi/misc/fastrpc.h
|
||||
|
||||
QUALCOMM HAMOA EMBEDDED CONTROLLER DRIVER
|
||||
M: Anvesh Jain P <anvesh.p@oss.qualcomm.com>
|
||||
M: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/embedded-controller/qcom,hamoa-crd-ec.yaml
|
||||
F: drivers/platform/arm64/qcom-hamoa-ec.c
|
||||
|
||||
QUALCOMM HEXAGON ARCHITECTURE
|
||||
M: Brian Cain <brian.cain@oss.qualcomm.com>
|
||||
L: linux-hexagon@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/olpc-ec.h>
|
||||
|
||||
|
|
@ -136,14 +137,16 @@ static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
|
|||
return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
|
||||
}
|
||||
|
||||
static int xo15_sci_add(struct acpi_device *device)
|
||||
static int xo15_sci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
int r;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
return -ENODEV;
|
||||
|
||||
strscpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME);
|
||||
strscpy(acpi_device_class(device), XO15_SCI_CLASS);
|
||||
|
|
@ -160,7 +163,7 @@ static int xo15_sci_add(struct acpi_device *device)
|
|||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe);
|
||||
dev_info(&pdev->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe);
|
||||
|
||||
r = sysfs_create_file(&device->dev.kobj, &lid_wake_on_close_attr.attr);
|
||||
if (r)
|
||||
|
|
@ -174,7 +177,7 @@ static int xo15_sci_add(struct acpi_device *device)
|
|||
|
||||
/* Enable wake-on-EC */
|
||||
if (device->wakeup.flags.valid)
|
||||
device_init_wakeup(&device->dev, true);
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -184,8 +187,11 @@ static int xo15_sci_add(struct acpi_device *device)
|
|||
return r;
|
||||
}
|
||||
|
||||
static void xo15_sci_remove(struct acpi_device *device)
|
||||
static void xo15_sci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
acpi_disable_gpe(NULL, xo15_sci_gpe);
|
||||
acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler);
|
||||
cancel_work_sync(&sci_work);
|
||||
|
|
@ -213,19 +219,18 @@ static const struct acpi_device_id xo15_sci_device_ids[] = {
|
|||
{"", 0},
|
||||
};
|
||||
|
||||
static struct acpi_driver xo15_sci_drv = {
|
||||
.name = DRV_NAME,
|
||||
.class = XO15_SCI_CLASS,
|
||||
.ids = xo15_sci_device_ids,
|
||||
.ops = {
|
||||
.add = xo15_sci_add,
|
||||
.remove = xo15_sci_remove,
|
||||
static struct platform_driver xo15_sci_drv = {
|
||||
.probe = xo15_sci_probe,
|
||||
.remove = xo15_sci_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.acpi_match_table = xo15_sci_device_ids,
|
||||
.pm = &xo15_sci_pm,
|
||||
},
|
||||
.drv.pm = &xo15_sci_pm,
|
||||
};
|
||||
|
||||
static int __init xo15_sci_init(void)
|
||||
{
|
||||
return acpi_bus_register_driver(&xo15_sci_drv);
|
||||
return platform_driver_register(&xo15_sci_drv);
|
||||
}
|
||||
device_initcall(xo15_sci_init);
|
||||
|
|
|
|||
|
|
@ -1248,6 +1248,21 @@ int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_bus_for_each_dev);
|
||||
|
||||
/**
|
||||
* acpi_bus_find_device_by_name() - Locate an ACPI device by its name
|
||||
* @name: Name of the device to match
|
||||
*
|
||||
* The caller is responsible for calling put_device() on the returned object.
|
||||
*
|
||||
* Returns:
|
||||
* New reference to the matched device or NULL if the device can't be found.
|
||||
*/
|
||||
struct device *acpi_bus_find_device_by_name(const char *name)
|
||||
{
|
||||
return bus_find_device_by_name(&acpi_bus_type, NULL, name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_bus_find_device_by_name);
|
||||
|
||||
struct acpi_dev_walk_context {
|
||||
int (*fn)(struct acpi_device *, void *);
|
||||
void *data;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/kfifo.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
|
@ -1124,8 +1125,8 @@ static int sonypi_acpi_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
|
||||
sonypi_acpi_device = device;
|
||||
strcpy(acpi_device_name(device), "Sony laptop hotkeys");
|
||||
strcpy(acpi_device_class(device), "sony/hotkey");
|
||||
strscpy(acpi_device_name(device), "Sony laptop hotkeys");
|
||||
strscpy(acpi_device_class(device), "sony/hotkey");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/align.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -36,10 +38,10 @@
|
|||
#include <linux/thermal.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include <linux/i8k.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#define I8K_SMM_FN_STATUS 0x0025
|
||||
#define I8K_SMM_POWER_STATUS 0x0069
|
||||
|
|
@ -232,7 +234,7 @@ static const struct dell_smm_ops i8k_smm_ops = {
|
|||
/*
|
||||
* Call the System Management Mode BIOS over WMI.
|
||||
*/
|
||||
static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg)
|
||||
static ssize_t wmi_parse_register(u8 *buffer, size_t length, unsigned int *reg)
|
||||
{
|
||||
__le32 value;
|
||||
u32 reg_size;
|
||||
|
|
@ -253,7 +255,7 @@ static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg)
|
|||
return reg_size + sizeof(reg_size);
|
||||
}
|
||||
|
||||
static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs)
|
||||
static int wmi_parse_response(u8 *buffer, size_t length, struct smm_regs *regs)
|
||||
{
|
||||
unsigned int *registers[] = {
|
||||
®s->eax,
|
||||
|
|
@ -261,7 +263,7 @@ static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs)
|
|||
®s->ecx,
|
||||
®s->edx
|
||||
};
|
||||
u32 offset = 0;
|
||||
size_t offset = 0;
|
||||
ssize_t ret;
|
||||
int i;
|
||||
|
||||
|
|
@ -273,19 +275,16 @@ static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset += ret;
|
||||
/* WMI aligns u32 integers on a 4 byte boundary */
|
||||
offset = ALIGN(offset + ret, 4);
|
||||
}
|
||||
|
||||
if (offset != length)
|
||||
return -ENOMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wmi_smm_call(struct device *dev, struct smm_regs *regs)
|
||||
{
|
||||
struct wmi_device *wdev = container_of(dev, struct wmi_device, dev);
|
||||
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
u32 wmi_payload[] = {
|
||||
sizeof(regs->eax),
|
||||
regs->eax,
|
||||
|
|
@ -296,34 +295,20 @@ static int wmi_smm_call(struct device *dev, struct smm_regs *regs)
|
|||
sizeof(regs->edx),
|
||||
regs->edx
|
||||
};
|
||||
const struct acpi_buffer in = {
|
||||
const struct wmi_buffer in = {
|
||||
.length = sizeof(wmi_payload),
|
||||
.pointer = &wmi_payload,
|
||||
.data = &wmi_payload,
|
||||
};
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
struct wmi_buffer out;
|
||||
int ret;
|
||||
|
||||
status = wmidev_evaluate_method(wdev, 0x0, DELL_SMM_LEGACY_EXECUTE, &in, &out);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
ret = wmidev_invoke_method(wdev, 0x0, DELL_SMM_LEGACY_EXECUTE, &in, &out, sizeof(__le32));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
obj = out.pointer;
|
||||
if (!obj)
|
||||
return -ENODATA;
|
||||
u8 *response __free(kfree) = out.data;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
ret = -ENOMSG;
|
||||
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = wmi_parse_response(obj->buffer.pointer, obj->buffer.length, regs);
|
||||
|
||||
err_free:
|
||||
kfree(obj);
|
||||
|
||||
return ret;
|
||||
return wmi_parse_response(response, out.length, regs);
|
||||
}
|
||||
|
||||
static int dell_smm_call(const struct dell_smm_ops *ops, struct smm_regs *regs)
|
||||
|
|
|
|||
|
|
@ -90,4 +90,17 @@ config EC_LENOVO_THINKPAD_T14S
|
|||
|
||||
Say M or Y here to include this support.
|
||||
|
||||
config EC_QCOM_HAMOA
|
||||
tristate "Embedded Controller driver for Qualcomm Hamoa/Glymur reference devices"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on I2C
|
||||
depends on THERMAL || THERMAL=n
|
||||
help
|
||||
Say M or Y here to enable the Embedded Controller driver for Qualcomm
|
||||
Snapdragon-based Hamoa/Glymur reference devices. The driver handles fan
|
||||
control, temperature sensors, access to EC state changes and supports
|
||||
reporting suspend entry/exit to the EC.
|
||||
|
||||
This driver currently supports Hamoa/Purwa/Glymur reference devices.
|
||||
|
||||
endif # ARM64_PLATFORM_DEVICES
|
||||
|
|
|
|||
|
|
@ -9,3 +9,4 @@ obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
|
|||
obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o
|
||||
obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
|
||||
obj-$(CONFIG_EC_LENOVO_THINKPAD_T14S) += lenovo-thinkpad-t14s.o
|
||||
obj-$(CONFIG_EC_QCOM_HAMOA) += qcom-hamoa-ec.o
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ static int aspire_ec_resume(struct device *dev)
|
|||
}
|
||||
|
||||
static const struct i2c_device_id aspire_ec_id[] = {
|
||||
{ "aspire1-ec", },
|
||||
{ .name = "aspire1-ec" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aspire_ec_id);
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@ static int gaokun_ec_probe(struct i2c_client *client)
|
|||
}
|
||||
|
||||
static const struct i2c_device_id gaokun_ec_id[] = {
|
||||
{ "gaokun-ec", },
|
||||
{ .name = "gaokun-ec" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, gaokun_ec_id);
|
||||
|
|
|
|||
|
|
@ -637,8 +637,8 @@ static const struct of_device_id t14s_ec_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, t14s_ec_of_match);
|
||||
|
||||
static const struct i2c_device_id t14s_ec_i2c_id_table[] = {
|
||||
{ "thinkpad-t14s-ec", },
|
||||
{}
|
||||
{ .name = "thinkpad-t14s-ec" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table);
|
||||
|
||||
|
|
|
|||
|
|
@ -238,8 +238,8 @@ static const struct of_device_id yoga_c630_ec_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match);
|
||||
|
||||
static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = {
|
||||
{ "yoga-c630-ec", },
|
||||
{}
|
||||
{ .name = "yoga-c630-ec" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table);
|
||||
|
||||
|
|
|
|||
451
drivers/platform/arm64/qcom-hamoa-ec.c
Normal file
451
drivers/platform/arm64/qcom-hamoa-ec.c
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2024 Maya Matuszczyk <maccraft123mc@gmail.com>
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define EC_SCI_EVT_READ_CMD 0x05
|
||||
#define EC_FW_VERSION_CMD 0x0e
|
||||
#define EC_MODERN_STANDBY_CMD 0x23
|
||||
#define EC_FAN_DBG_CONTROL_CMD 0x30
|
||||
#define EC_SCI_EVT_CONTROL_CMD 0x35
|
||||
#define EC_THERMAL_CAP_CMD 0x42
|
||||
|
||||
#define EC_FW_VERSION_RESP_LEN 4
|
||||
#define EC_THERMAL_CAP_RESP_LEN 3
|
||||
#define EC_FAN_DEBUG_CMD_LEN 6
|
||||
#define EC_FAN_SPEED_DATA_SIZE 4
|
||||
|
||||
#define EC_MODERN_STANDBY_ENTER 0x01
|
||||
#define EC_MODERN_STANDBY_EXIT 0x00
|
||||
|
||||
#define EC_FAN_DEBUG_MODE_OFF 0
|
||||
#define EC_FAN_DEBUG_MODE_ON BIT(0)
|
||||
#define EC_FAN_ON BIT(1)
|
||||
#define EC_FAN_DEBUG_TYPE_PWM BIT(2)
|
||||
#define EC_MAX_FAN_CNT 2
|
||||
#define EC_FAN_NAME_SIZE 20
|
||||
#define EC_FAN_MAX_PWM 255
|
||||
|
||||
enum qcom_ec_sci_events {
|
||||
EC_FAN1_STATUS_CHANGE_EVT = 0x30,
|
||||
EC_FAN2_STATUS_CHANGE_EVT,
|
||||
EC_FAN1_SPEED_CHANGE_EVT,
|
||||
EC_FAN2_SPEED_CHANGE_EVT,
|
||||
EC_NEW_LUT_SET_EVT,
|
||||
EC_FAN_PROFILE_SWITCH_EVT,
|
||||
EC_THERMISTOR_1_THRESHOLD_CROSS_EVT,
|
||||
EC_THERMISTOR_2_THRESHOLD_CROSS_EVT,
|
||||
EC_THERMISTOR_3_THRESHOLD_CROSS_EVT,
|
||||
/* Reserved: 0x39 - 0x3c/0x3f */
|
||||
EC_RECOVERED_FROM_RESET_EVT = 0x3d,
|
||||
};
|
||||
|
||||
struct qcom_ec_version {
|
||||
u8 main_version;
|
||||
u8 sub_version;
|
||||
u8 test_version;
|
||||
};
|
||||
|
||||
struct qcom_ec_thermal_cap {
|
||||
#define EC_THERMAL_FAN_CNT(x) (FIELD_GET(GENMASK(1, 0), (x)))
|
||||
#define EC_THERMAL_FAN_TYPE(x) (FIELD_GET(GENMASK(4, 2), (x)))
|
||||
#define EC_THERMAL_THERMISTOR_MASK(x) (FIELD_GET(GENMASK(7, 0), (x)))
|
||||
u8 fan_cnt;
|
||||
u8 fan_type;
|
||||
u8 thermistor_mask;
|
||||
};
|
||||
|
||||
struct qcom_ec_cooling_dev {
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct device *parent_dev;
|
||||
u8 fan_id;
|
||||
u8 state;
|
||||
};
|
||||
|
||||
struct qcom_ec {
|
||||
struct qcom_ec_cooling_dev *ec_cdev;
|
||||
struct qcom_ec_thermal_cap thermal_cap;
|
||||
struct qcom_ec_version version;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int qcom_ec_read(struct qcom_ec *ec, u8 cmd, u8 resp_len, u8 *resp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(ec->client, cmd, resp_len, resp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret == 0 || ret == 0xff)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (resp[0] >= resp_len)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EC Device Firmware Version:
|
||||
*
|
||||
* Read Response:
|
||||
* ----------------------------------------------------------------------
|
||||
* | Offset | Name | Description |
|
||||
* ----------------------------------------------------------------------
|
||||
* | 0x00 | Byte count | Number of bytes in response |
|
||||
* | | | (excluding byte count) |
|
||||
* ----------------------------------------------------------------------
|
||||
* | 0x01 | Test-version | Test-version of EC firmware |
|
||||
* ----------------------------------------------------------------------
|
||||
* | 0x02 | Sub-version | Sub-version of EC firmware |
|
||||
* ----------------------------------------------------------------------
|
||||
* | 0x03 | Main-version | Main-version of EC firmware |
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
static int qcom_ec_read_fw_version(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct qcom_ec *ec = i2c_get_clientdata(client);
|
||||
struct qcom_ec_version *version = &ec->version;
|
||||
u8 resp[EC_FW_VERSION_RESP_LEN];
|
||||
int ret;
|
||||
|
||||
ret = qcom_ec_read(ec, EC_FW_VERSION_CMD, EC_FW_VERSION_RESP_LEN, resp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
version->main_version = resp[3];
|
||||
version->sub_version = resp[2];
|
||||
version->test_version = resp[1];
|
||||
|
||||
dev_dbg(dev, "EC Version %d.%d.%d\n",
|
||||
version->main_version, version->sub_version, version->test_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EC Device Thermal Capabilities:
|
||||
*
|
||||
* Read Response:
|
||||
* ------------------------------------------------------------------------------
|
||||
* | Offset | Name | Description |
|
||||
* ------------------------------------------------------------------------------
|
||||
* | 0x00 | Byte count | Number of bytes in response |
|
||||
* | | | (excluding byte count) |
|
||||
* ------------------------------------------------------------------------------
|
||||
* | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans |
|
||||
* | 0x03 | Capabilities | Bit 2-4: Type of fan |
|
||||
* | | | Bit 5-6: Reserved |
|
||||
* | | | Bit 7: Data Valid/Invalid |
|
||||
* | | | (Valid - 1, Invalid - 0) |
|
||||
* | | | Bit 8-15: Thermistor 0 - 7 presence |
|
||||
* | | | (1 present, 0 absent) |
|
||||
* ------------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
static int qcom_ec_thermal_capabilities(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct qcom_ec *ec = i2c_get_clientdata(client);
|
||||
struct qcom_ec_thermal_cap *cap = &ec->thermal_cap;
|
||||
u8 resp[EC_THERMAL_CAP_RESP_LEN];
|
||||
int ret;
|
||||
|
||||
ret = qcom_ec_read(ec, EC_THERMAL_CAP_CMD, EC_THERMAL_CAP_RESP_LEN, resp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cap->fan_cnt = min(EC_MAX_FAN_CNT, EC_THERMAL_FAN_CNT(resp[1]));
|
||||
cap->fan_type = EC_THERMAL_FAN_TYPE(resp[1]);
|
||||
cap->thermistor_mask = EC_THERMAL_THERMISTOR_MASK(resp[2]);
|
||||
|
||||
dev_dbg(dev, "Fan count: %d Fan Type: %d Thermistor Mask: %x\n",
|
||||
cap->fan_cnt, cap->fan_type, cap->thermistor_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t qcom_ec_irq(int irq, void *data)
|
||||
{
|
||||
struct qcom_ec *ec = data;
|
||||
struct device *dev = &ec->client->dev;
|
||||
int val;
|
||||
|
||||
val = i2c_smbus_read_byte_data(ec->client, EC_SCI_EVT_READ_CMD);
|
||||
if (val < 0) {
|
||||
dev_err_ratelimited(dev, "Failed to read EC SCI Event: %d\n", val);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case EC_FAN1_STATUS_CHANGE_EVT:
|
||||
dev_dbg_ratelimited(dev, "Fan1 status changed\n");
|
||||
break;
|
||||
case EC_FAN2_STATUS_CHANGE_EVT:
|
||||
dev_dbg_ratelimited(dev, "Fan2 status changed\n");
|
||||
break;
|
||||
case EC_FAN1_SPEED_CHANGE_EVT:
|
||||
dev_dbg_ratelimited(dev, "Fan1 speed crossed low/high trip point\n");
|
||||
break;
|
||||
case EC_FAN2_SPEED_CHANGE_EVT:
|
||||
dev_dbg_ratelimited(dev, "Fan2 speed crossed low/high trip point\n");
|
||||
break;
|
||||
case EC_NEW_LUT_SET_EVT:
|
||||
dev_dbg_ratelimited(dev, "New LUT set\n");
|
||||
break;
|
||||
case EC_FAN_PROFILE_SWITCH_EVT:
|
||||
dev_dbg_ratelimited(dev, "FAN Profile switched\n");
|
||||
break;
|
||||
case EC_THERMISTOR_1_THRESHOLD_CROSS_EVT:
|
||||
dev_dbg_ratelimited(dev, "Thermistor 1 threshold crossed\n");
|
||||
break;
|
||||
case EC_THERMISTOR_2_THRESHOLD_CROSS_EVT:
|
||||
dev_dbg_ratelimited(dev, "Thermistor 2 threshold crossed\n");
|
||||
break;
|
||||
case EC_THERMISTOR_3_THRESHOLD_CROSS_EVT:
|
||||
dev_dbg_ratelimited(dev, "Thermistor 3 threshold crossed\n");
|
||||
break;
|
||||
case EC_RECOVERED_FROM_RESET_EVT:
|
||||
dev_dbg_ratelimited(dev, "EC recovered from reset\n");
|
||||
break;
|
||||
default:
|
||||
dev_notice_ratelimited(dev, "Unknown EC event: %d\n", val);
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qcom_ec_sci_evt_control(struct device *dev, bool enable)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(client, EC_SCI_EVT_CONTROL_CMD, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
static int qcom_ec_fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
|
||||
{
|
||||
*state = EC_FAN_MAX_PWM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_ec_fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
|
||||
{
|
||||
struct qcom_ec_cooling_dev *ec_cdev = cdev->devdata;
|
||||
|
||||
*state = ec_cdev->state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fan Debug control command:
|
||||
*
|
||||
* Command Payload:
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | Offset | Name | Description |
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | 0x00 | Command | Fan control command |
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | 0x01 | Fan ID | 0x1 : Fan 1 |
|
||||
* | | | 0x2 : Fan 2 |
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | 0x02 | Byte count = 4| Size of data to set fan speed |
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | 0x03 | Mode | Bit 0: Debug Mode On/Off (0 - OFF, 1 - ON ) |
|
||||
* | | | Bit 1: Fan On/Off (0 - Off, 1 - ON) |
|
||||
* | | | Bit 2: Debug Type (0 - RPM, 1 - PWM) |
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM |
|
||||
* | 0x05 | | |
|
||||
* --------------------------------------------------------------------------------------
|
||||
* | 0x06 | Speed in PWM | PWM value, if mode selected is PWM (0 - 255) |
|
||||
* ______________________________________________________________________________________
|
||||
*
|
||||
*/
|
||||
static int qcom_ec_fan_debug_mode_off(struct qcom_ec_cooling_dev *ec_cdev)
|
||||
{
|
||||
struct device *dev = ec_cdev->parent_dev;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 request[6] = { ec_cdev->fan_id, EC_FAN_SPEED_DATA_SIZE,
|
||||
EC_FAN_DEBUG_MODE_OFF, 0, 0, 0 };
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, EC_FAN_DBG_CONTROL_CMD,
|
||||
sizeof(request), request);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to turn off fan%d debug mode: %d\n",
|
||||
ec_cdev->fan_id, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_ec_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
|
||||
{
|
||||
struct qcom_ec_cooling_dev *ec_cdev = cdev->devdata;
|
||||
struct device *dev = ec_cdev->parent_dev;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 request[6] = { ec_cdev->fan_id, EC_FAN_SPEED_DATA_SIZE,
|
||||
EC_FAN_DEBUG_MODE_ON | EC_FAN_ON | EC_FAN_DEBUG_TYPE_PWM,
|
||||
0, 0, state };
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, EC_FAN_DBG_CONTROL_CMD,
|
||||
sizeof(request), request);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set fan pwm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ec_cdev->state = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops qcom_ec_thermal_ops = {
|
||||
.get_max_state = qcom_ec_fan_get_max_state,
|
||||
.get_cur_state = qcom_ec_fan_get_cur_state,
|
||||
.set_cur_state = qcom_ec_fan_set_cur_state,
|
||||
};
|
||||
|
||||
static int qcom_ec_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(client, EC_MODERN_STANDBY_CMD,
|
||||
EC_MODERN_STANDBY_EXIT);
|
||||
}
|
||||
|
||||
static int qcom_ec_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(client, EC_MODERN_STANDBY_CMD,
|
||||
EC_MODERN_STANDBY_ENTER);
|
||||
}
|
||||
|
||||
static int qcom_ec_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct qcom_ec *ec;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
|
||||
if (!ec)
|
||||
return -ENOMEM;
|
||||
|
||||
ec->client = client;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL, qcom_ec_irq,
|
||||
IRQF_ONESHOT, "qcom_ec", ec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, ec);
|
||||
|
||||
ret = qcom_ec_read_fw_version(dev);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to read EC firmware version\n");
|
||||
|
||||
ret = qcom_ec_sci_evt_control(dev, true);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to enable SCI events\n");
|
||||
|
||||
ret = qcom_ec_thermal_capabilities(dev);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to read thermal capabilities\n");
|
||||
|
||||
if (ec->thermal_cap.fan_cnt == 0) {
|
||||
dev_warn(dev, FW_BUG "Failed to get fan count, firmware update required\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ec->ec_cdev = devm_kcalloc(dev, ec->thermal_cap.fan_cnt, sizeof(*ec->ec_cdev), GFP_KERNEL);
|
||||
if (!ec->ec_cdev)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ec->thermal_cap.fan_cnt; i++) {
|
||||
struct qcom_ec_cooling_dev *ec_cdev = &ec->ec_cdev[i];
|
||||
char name[EC_FAN_NAME_SIZE];
|
||||
|
||||
scnprintf(name, sizeof(name), "qcom_ec_fan_%u", i);
|
||||
ec_cdev->fan_id = i + 1;
|
||||
ec_cdev->parent_dev = dev;
|
||||
|
||||
ec_cdev->cdev = devm_thermal_of_child_cooling_device_register(dev, NULL, name, ec_cdev,
|
||||
&qcom_ec_thermal_ops);
|
||||
if (IS_ERR(ec_cdev->cdev)) {
|
||||
return dev_err_probe(dev, PTR_ERR(ec_cdev->cdev),
|
||||
"Failed to register fan%d cooling device\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_ec_remove(struct i2c_client *client)
|
||||
{
|
||||
struct qcom_ec *ec = i2c_get_clientdata(client);
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
ret = qcom_ec_sci_evt_control(dev, false);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to disable SCI events: %d\n", ret);
|
||||
|
||||
for (int i = 0; i < ec->thermal_cap.fan_cnt; i++) {
|
||||
struct qcom_ec_cooling_dev *ec_cdev = &ec->ec_cdev[i];
|
||||
|
||||
qcom_ec_fan_debug_mode_off(ec_cdev);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_ec_of_match[] = {
|
||||
{ .compatible = "qcom,hamoa-crd-ec" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_ec_of_match);
|
||||
|
||||
static const struct i2c_device_id qcom_ec_i2c_id_table[] = {
|
||||
{ "qcom-hamoa-ec", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, qcom_ec_i2c_id_table);
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(qcom_ec_pm_ops,
|
||||
qcom_ec_suspend,
|
||||
qcom_ec_resume);
|
||||
|
||||
static struct i2c_driver qcom_ec_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-hamoa-ec",
|
||||
.of_match_table = qcom_ec_of_match,
|
||||
.pm = &qcom_ec_pm_ops,
|
||||
},
|
||||
.probe = qcom_ec_probe,
|
||||
.remove = qcom_ec_remove,
|
||||
.id_table = qcom_ec_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(qcom_ec_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QCOM Hamoa Embedded Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -420,6 +420,19 @@ static const struct software_node *ssam_node_group_sp11[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Pro 12" first edition (ARM/QCOM) */
|
||||
static const struct software_node *ssam_node_group_sp12in[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_hub_kip,
|
||||
&ssam_node_tmp_sensors,
|
||||
&ssam_node_hid_kip_keyboard,
|
||||
&ssam_node_hid_sam_penstash,
|
||||
&ssam_node_hid_kip_touchpad,
|
||||
&ssam_node_hid_kip_fwupd,
|
||||
&ssam_node_pos_tablet_switch,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
|
||||
|
||||
static const struct acpi_device_id ssam_platform_hub_acpi_match[] = {
|
||||
|
|
@ -498,6 +511,8 @@ static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = {
|
|||
{ .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g },
|
||||
/* Surface Pro 11 (ARM/QCOM) */
|
||||
{ .compatible = "microsoft,denali", (void *)ssam_node_group_sp11 },
|
||||
/* Surface Pro 12in First Edition (ARM/QCOM) */
|
||||
{ .compatible = "microsoft,surface-pro-12in", (void *)ssam_node_group_sp12in },
|
||||
/* Surface Laptop 7 */
|
||||
{ .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 },
|
||||
{ .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 },
|
||||
|
|
|
|||
|
|
@ -858,7 +858,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static const DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -867,7 +868,8 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(guid);
|
||||
|
||||
static const DEVICE_ATTR_RO(guid);
|
||||
|
||||
static ssize_t instance_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
|
@ -876,7 +878,8 @@ static ssize_t instance_count_show(struct device *dev,
|
|||
|
||||
return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count);
|
||||
}
|
||||
static DEVICE_ATTR_RO(instance_count);
|
||||
|
||||
static const DEVICE_ATTR_RO(instance_count);
|
||||
|
||||
static ssize_t expensive_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
|
@ -886,9 +889,10 @@ static ssize_t expensive_show(struct device *dev,
|
|||
return sysfs_emit(buf, "%d\n",
|
||||
(wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
|
||||
}
|
||||
static DEVICE_ATTR_RO(expensive);
|
||||
|
||||
static struct attribute *wmi_attrs[] = {
|
||||
static const DEVICE_ATTR_RO(expensive);
|
||||
|
||||
static const struct attribute * const wmi_attrs[] = {
|
||||
&dev_attr_modalias.attr,
|
||||
&dev_attr_guid.attr,
|
||||
&dev_attr_instance_count.attr,
|
||||
|
|
@ -904,9 +908,10 @@ static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(notify_id);
|
||||
|
||||
static struct attribute *wmi_event_attrs[] = {
|
||||
static const DEVICE_ATTR_RO(notify_id);
|
||||
|
||||
static const struct attribute * const wmi_event_attrs[] = {
|
||||
&dev_attr_notify_id.attr,
|
||||
NULL
|
||||
};
|
||||
|
|
@ -920,7 +925,8 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
|
|||
return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0],
|
||||
wblock->gblock.object_id[1]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(object_id);
|
||||
|
||||
static const DEVICE_ATTR_RO(object_id);
|
||||
|
||||
static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -929,16 +935,17 @@ static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return sysfs_emit(buf, "%d\n", (int)wdev->setable);
|
||||
}
|
||||
static DEVICE_ATTR_RO(setable);
|
||||
|
||||
static struct attribute *wmi_data_attrs[] = {
|
||||
static const DEVICE_ATTR_RO(setable);
|
||||
|
||||
static const struct attribute * const wmi_data_attrs[] = {
|
||||
&dev_attr_object_id.attr,
|
||||
&dev_attr_setable.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wmi_data);
|
||||
|
||||
static struct attribute *wmi_method_attrs[] = {
|
||||
static const struct attribute * const wmi_method_attrs[] = {
|
||||
&dev_attr_object_id.attr,
|
||||
NULL
|
||||
};
|
||||
|
|
@ -1088,7 +1095,7 @@ static void wmi_dev_shutdown(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static struct class wmi_bus_class = {
|
||||
static const struct class wmi_bus_class = {
|
||||
.name = "wmi_bus",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ config AYANEO_EC
|
|||
config MERAKI_MX100
|
||||
tristate "Cisco Meraki MX100 Platform Driver"
|
||||
depends on GPIOLIB
|
||||
depends on LPC_ICH
|
||||
depends on GPIO_ICH
|
||||
depends on LEDS_CLASS
|
||||
select LEDS_GPIO
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@
|
|||
|
||||
#define AMD_HFI_DRIVER "amd_hfi"
|
||||
#define AMD_HFI_MAILBOX_COUNT 1
|
||||
#define AMD_HETERO_RANKING_TABLE_VER 2
|
||||
#define AMD_HETERO_RANKING_TABLE_MIN_VER 2
|
||||
#define AMD_HETERO_RANKING_TABLE_MAX_VER 3
|
||||
|
||||
#define AMD_HETERO_CPUID_27 0x80000027
|
||||
|
||||
|
|
@ -158,7 +159,8 @@ static int amd_hfi_fill_metadata(struct amd_hfi_data *amd_hfi_data)
|
|||
dev_err(amd_hfi_data->dev, "invalid signature in shared memory\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (amd_hfi_data->shmem->version_number != AMD_HETERO_RANKING_TABLE_VER) {
|
||||
if (amd_hfi_data->shmem->version_number < AMD_HETERO_RANKING_TABLE_MIN_VER ||
|
||||
amd_hfi_data->shmem->version_number > AMD_HETERO_RANKING_TABLE_MAX_VER) {
|
||||
dev_err(amd_hfi_data->dev, "invalid version %d\n",
|
||||
amd_hfi_data->shmem->version_number);
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ static int validate_message(struct hsmp_message *msg)
|
|||
int hsmp_send_message(struct hsmp_message *msg)
|
||||
{
|
||||
struct hsmp_socket *sock;
|
||||
unsigned int sock_ind;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
|
|
@ -212,7 +213,15 @@ int hsmp_send_message(struct hsmp_message *msg)
|
|||
|
||||
if (!hsmp_pdev.sock || msg->sock_ind >= hsmp_pdev.num_sockets)
|
||||
return -ENODEV;
|
||||
sock = &hsmp_pdev.sock[msg->sock_ind];
|
||||
|
||||
/*
|
||||
* Sanitize sock_ind after the bounds check. A mispredicted branch can
|
||||
* still let the CPU speculatively use msg->sock_ind as an index into
|
||||
* hsmp_pdev.sock[] (Spectre v1, CVE-2017-5753), including for callers
|
||||
* other than hsmp_ioctl_msg() that pass a user-derived socket index.
|
||||
*/
|
||||
sock_ind = array_index_nospec(msg->sock_ind, hsmp_pdev.num_sockets);
|
||||
sock = &hsmp_pdev.sock[sock_ind];
|
||||
|
||||
ret = down_interruptible(&sock->hsmp_sem);
|
||||
if (ret < 0)
|
||||
|
|
@ -308,6 +317,19 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
|
|||
if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX)
|
||||
return -ENOMSG;
|
||||
|
||||
/*
|
||||
* Sanitize the user-controlled msg_id against speculative
|
||||
* execution. The bounds check above retires the out-of-range
|
||||
* case with -ENOMSG, but a mispredicted branch can still let the
|
||||
* CPU speculatively use msg_id as an index into
|
||||
* hsmp_msg_desc_table[] (here and in validate_message() /
|
||||
* is_get_msg() called downstream via hsmp_send_message()), and
|
||||
* pull arbitrary kernel memory into the cache (Spectre v1,
|
||||
* CVE-2017-5753). Clamp once into msg.msg_id so every downstream
|
||||
* dereference sees the sanitized value.
|
||||
*/
|
||||
msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX);
|
||||
|
||||
switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) {
|
||||
case FMODE_WRITE:
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
struct quirk_entry {
|
||||
u32 s2idle_bug_mmio;
|
||||
bool spurious_8042;
|
||||
bool need_suspend_delay;
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_s2idle_bug = {
|
||||
|
|
@ -33,6 +34,10 @@ static struct quirk_entry quirk_s2idle_spurious_8042 = {
|
|||
.spurious_8042 = true,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_s2idle_need_suspend_delay = {
|
||||
.need_suspend_delay = true,
|
||||
};
|
||||
|
||||
static const struct dmi_system_id fwbug_list[] = {
|
||||
{
|
||||
.ident = "L14 Gen2 AMD",
|
||||
|
|
@ -203,6 +208,35 @@ static const struct dmi_system_id fwbug_list[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"),
|
||||
}
|
||||
},
|
||||
/* https://bugzilla.kernel.org/show_bug.cgi?id=221383 */
|
||||
{
|
||||
.ident = "Zen3-based IdeaPad Slim and similar",
|
||||
.driver_data = &quirk_s2idle_need_suspend_delay,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
||||
/*
|
||||
* Note: there are also some Zen2-based 82X* devices that
|
||||
* need different quirks, they're already handled above
|
||||
*/
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "82X"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.ident = "Zen3+-based IdeaPad Slim and similar",
|
||||
.driver_data = &quirk_s2idle_need_suspend_delay,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "83K"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.ident = "IdeaPad Slim 3 15ARP10 (83MM)",
|
||||
.driver_data = &quirk_s2idle_need_suspend_delay,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "83MM"),
|
||||
}
|
||||
},
|
||||
/* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */
|
||||
{
|
||||
.ident = "Thinkpad L14 Gen3",
|
||||
|
|
@ -356,6 +390,11 @@ void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev)
|
|||
amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio);
|
||||
}
|
||||
|
||||
bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev)
|
||||
{
|
||||
return dev->quirks && dev->quirks->need_suspend_delay;
|
||||
}
|
||||
|
||||
void amd_pmc_quirks_init(struct amd_pmc_dev *dev)
|
||||
{
|
||||
const struct dmi_system_id *dmi_id;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/bits.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/limits.h>
|
||||
|
|
@ -85,10 +86,109 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = {
|
|||
{"VPE", BIT(21)},
|
||||
};
|
||||
|
||||
/* CPU info structures for different SoC variants */
|
||||
static const struct amd_pmc_cpu_info amd_pco_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MESSAGE,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = 12,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_PCO,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_czn_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MESSAGE,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = 12,
|
||||
.scratch_reg = AMD_PMC_SCRATCH_REG_CZN,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_vg_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MESSAGE,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = 12,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_yc_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MESSAGE,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = 12,
|
||||
.scratch_reg = AMD_PMC_SCRATCH_REG_YC,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_ps_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MESSAGE,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = 21,
|
||||
.scratch_reg = AMD_PMC_SCRATCH_REG_YC,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_1ah_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MSG_1AH_20H,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = ARRAY_SIZE(soc15_ip_blk),
|
||||
.scratch_reg = AMD_PMC_SCRATCH_REG_1AH,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_1ah_m70_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MSG_1AH_20H,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARGUMENT,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RESPONSE,
|
||||
.num_ips = ARRAY_SIZE(soc15_ip_blk_v2),
|
||||
.scratch_reg = AMD_PMC_SCRATCH_REG_1AH,
|
||||
.ips_ptr = soc15_ip_blk_v2,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct amd_pmc_cpu_info amd_1ah_m80_cpu_info = {
|
||||
.smu_msg = AMD_PMC_REGISTER_MSG_1AH_80H,
|
||||
.smu_arg = AMD_PMC_REGISTER_ARG_1AH_80H,
|
||||
.smu_rsp = AMD_PMC_REGISTER_RSP_1AH_80H,
|
||||
.num_ips = ARRAY_SIZE(soc15_ip_blk),
|
||||
.scratch_reg = AMD_PMC_SCRATCH_REG_1AH,
|
||||
.ips_ptr = soc15_ip_blk,
|
||||
.os_hint = MSG_OS_HINT_RN,
|
||||
};
|
||||
|
||||
static const struct pci_device_id pmc_pci_ids[] = {
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_PCO, &amd_pco_cpu_info) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_CZN, &amd_czn_cpu_info) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_VG, &amd_vg_cpu_info) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_YC, &amd_yc_cpu_info) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_CB, &amd_yc_cpu_info) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_PS, &amd_ps_cpu_info) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_SP, NULL) },
|
||||
{ PCI_DEVICE_DATA(AMD, CPU_ID_SHP, NULL) },
|
||||
{ PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, NULL) },
|
||||
{ PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, NULL) },
|
||||
{ PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT, &amd_1ah_m80_cpu_info) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static bool disable_workarounds;
|
||||
module_param(disable_workarounds, bool, 0644);
|
||||
MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs");
|
||||
|
||||
static int delay_suspend = -1;
|
||||
module_param(delay_suspend, int, 0644);
|
||||
MODULE_PARM_DESC(delay_suspend,
|
||||
"Delays s2idle by 2.5 seconds to work around buggy ECs, often causing keyboard issues after suspend. 0: don't delay, 1: do delay, -1 (default): let amd_pmc decide. If you need this please report this to: platform-driver-x86@vger.kernel.org");
|
||||
|
||||
static struct amd_pmc_dev pmc;
|
||||
|
||||
static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
|
||||
|
|
@ -101,35 +201,40 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3
|
|||
iowrite32(val, dev->regbase + reg_offset);
|
||||
}
|
||||
|
||||
static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
|
||||
static int amd_pmc_set_cpu_info(struct amd_pmc_dev *dev, struct pci_dev *rdev)
|
||||
{
|
||||
const struct pci_device_id *id;
|
||||
|
||||
id = pci_match_id(pmc_pci_ids, rdev);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
dev->cpu_id = rdev->device;
|
||||
|
||||
if (id->driver_data) {
|
||||
dev->cpu_info = (const struct amd_pmc_cpu_info *)id->driver_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Special case: 1Ah M20H/M60H needs x86_model detection */
|
||||
switch (dev->cpu_id) {
|
||||
case AMD_CPU_ID_PCO:
|
||||
case AMD_CPU_ID_RN:
|
||||
case AMD_CPU_ID_VG:
|
||||
case AMD_CPU_ID_YC:
|
||||
case AMD_CPU_ID_CB:
|
||||
dev->num_ips = 12;
|
||||
dev->ips_ptr = soc15_ip_blk;
|
||||
dev->smu_msg = 0x538;
|
||||
break;
|
||||
case AMD_CPU_ID_PS:
|
||||
dev->num_ips = 21;
|
||||
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:
|
||||
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;
|
||||
if (boot_cpu_data.x86_model == 0x70)
|
||||
dev->cpu_info = &amd_1ah_m70_cpu_info;
|
||||
else
|
||||
dev->cpu_info = &amd_1ah_cpu_info;
|
||||
break;
|
||||
case AMD_CPU_ID_SP:
|
||||
case AMD_CPU_ID_SHP:
|
||||
dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
|
||||
return -ENODEV;
|
||||
default:
|
||||
dev_err(dev->dev, "Unknown CPU ID: 0x%x\n", dev->cpu_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
|
||||
|
|
@ -296,9 +401,9 @@ static int smu_fw_info_show(struct seq_file *s, void *unused)
|
|||
table.timeto_resume_to_os_lastcapture);
|
||||
|
||||
seq_puts(s, "\n=== Active time (in us) ===\n");
|
||||
for (idx = 0 ; idx < dev->num_ips ; idx++) {
|
||||
if (dev->ips_ptr[idx].bit_mask & dev->active_ips)
|
||||
seq_printf(s, "%-8s : %lld\n", dev->ips_ptr[idx].name,
|
||||
for (idx = 0 ; idx < dev->cpu_info->num_ips ; idx++) {
|
||||
if (dev->cpu_info->ips_ptr[idx].bit_mask & dev->active_ips)
|
||||
seq_printf(s, "%-8s : %lld\n", dev->cpu_info->ips_ptr[idx].name,
|
||||
table.timecondition_notmet_lastcapture[idx]);
|
||||
}
|
||||
|
||||
|
|
@ -347,32 +452,22 @@ static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
|
|||
u32 val;
|
||||
int rc;
|
||||
|
||||
switch (pdev->cpu_id) {
|
||||
case AMD_CPU_ID_CZN:
|
||||
/* we haven't yet read SMU version */
|
||||
/* we haven't yet read SMU version */
|
||||
if (pdev->cpu_id == AMD_CPU_ID_CZN) {
|
||||
if (!pdev->major) {
|
||||
rc = amd_pmc_get_smu_version(pdev);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37))
|
||||
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
|
||||
else
|
||||
if (!(pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37)))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case AMD_CPU_ID_YC:
|
||||
case AMD_CPU_ID_CB:
|
||||
case AMD_CPU_ID_PS:
|
||||
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
|
||||
break;
|
||||
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
|
||||
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_1AH);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdev->cpu_info->scratch_reg)
|
||||
return -EINVAL;
|
||||
|
||||
val = amd_pmc_reg_read(pdev, pdev->cpu_info->scratch_reg);
|
||||
|
||||
if (dev)
|
||||
pm_pr_dbg("SMU idlemask s0i3: 0x%x\n", val);
|
||||
|
||||
|
|
@ -425,9 +520,9 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
|
|||
argument = dev->stb_arg.arg;
|
||||
response = dev->stb_arg.resp;
|
||||
} else {
|
||||
message = dev->smu_msg;
|
||||
argument = AMD_PMC_REGISTER_ARGUMENT;
|
||||
response = AMD_PMC_REGISTER_RESPONSE;
|
||||
message = dev->cpu_info->smu_msg;
|
||||
argument = dev->cpu_info->smu_arg;
|
||||
response = dev->cpu_info->smu_rsp;
|
||||
}
|
||||
|
||||
value = amd_pmc_reg_read(dev, response);
|
||||
|
|
@ -452,9 +547,9 @@ int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool r
|
|||
argument = dev->stb_arg.arg;
|
||||
response = dev->stb_arg.resp;
|
||||
} else {
|
||||
message = dev->smu_msg;
|
||||
argument = AMD_PMC_REGISTER_ARGUMENT;
|
||||
response = AMD_PMC_REGISTER_RESPONSE;
|
||||
message = dev->cpu_info->smu_msg;
|
||||
argument = dev->cpu_info->smu_arg;
|
||||
response = dev->cpu_info->smu_rsp;
|
||||
}
|
||||
|
||||
/* Wait until we get a valid response */
|
||||
|
|
@ -512,23 +607,6 @@ int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool r
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
|
||||
{
|
||||
switch (dev->cpu_id) {
|
||||
case AMD_CPU_ID_PCO:
|
||||
return MSG_OS_HINT_PCO;
|
||||
case AMD_CPU_ID_RN:
|
||||
case AMD_CPU_ID_VG:
|
||||
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 MSG_OS_HINT_RN;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev)
|
||||
{
|
||||
struct device *d;
|
||||
|
|
@ -598,13 +676,81 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev)
|
||||
{
|
||||
/*
|
||||
* Starting a new HW sleep cycle right after waking from one
|
||||
* can cause electrical problems triggering the over voltage protection.
|
||||
* That is avoided by delaying the next suspend a bit, see also
|
||||
* https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/
|
||||
*/
|
||||
struct smu_metrics table;
|
||||
|
||||
return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status;
|
||||
}
|
||||
|
||||
static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev)
|
||||
{
|
||||
/*
|
||||
* intermediate_wakeup implies that the machine didn't get to deepest sleep
|
||||
* state before - otherwise this function isn't called in amd_pmc_s2idle_check()
|
||||
* because amd_pmc_intermediate_wakeup_need_delay() returns true first.
|
||||
* On some IdeaPads that happens when charging, because the EC seems
|
||||
* to send lots of messages then that wake the machine.
|
||||
*
|
||||
* But even in that case, the sleep here is necessary (on those IdeaPads),
|
||||
* otherwise they wake up completely (resume) after a few seconds.
|
||||
* So this variable is only used to avoid spamming dmesg on each
|
||||
* intermediate wakeup.
|
||||
*/
|
||||
bool intermediate_wakeup = !pdev->is_first_check_after_suspend;
|
||||
|
||||
/*
|
||||
* Some Lenovo Laptops (like different IdeaPad 3 Slims) need some
|
||||
* me-time before sleeping or they get uncooperative after waking
|
||||
* up and don't send events for keyboard and lid switch anymore.
|
||||
*
|
||||
* Unfortunately this doesn't entirely fix the problem: It can still
|
||||
* happen when resuming with a timer (wakealarm), but at least the
|
||||
* more common usecases (wakeup by opening lid or pressing a key)
|
||||
* work fine with this workaround.
|
||||
*
|
||||
* See https://bugzilla.kernel.org/show_bug.cgi?id=221383
|
||||
*/
|
||||
if (amd_pmc_quirk_need_suspend_delay(pdev)) {
|
||||
/*
|
||||
* delay_suspend=1 force-enables this, otherwise it can be
|
||||
* disabled with disable_workarounds or delay_suspend=0
|
||||
*/
|
||||
if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) {
|
||||
if (!intermediate_wakeup)
|
||||
dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n");
|
||||
return true;
|
||||
}
|
||||
if (!intermediate_wakeup)
|
||||
dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n");
|
||||
} else if (delay_suspend == 1) {
|
||||
if (!intermediate_wakeup)
|
||||
dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n",
|
||||
dmi_get_system_info(DMI_SYS_VENDOR),
|
||||
dmi_get_system_info(DMI_PRODUCT_NAME),
|
||||
dmi_get_system_info(DMI_PRODUCT_FAMILY),
|
||||
dmi_get_system_info(DMI_BOARD_VENDOR),
|
||||
dmi_get_system_info(DMI_BOARD_NAME));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void amd_pmc_s2idle_prepare(void)
|
||||
{
|
||||
struct amd_pmc_dev *pdev = &pmc;
|
||||
int rc;
|
||||
u8 msg;
|
||||
u32 arg = 1;
|
||||
|
||||
/* Reset this variable because this is a fresh suspend */
|
||||
pdev->is_first_check_after_suspend = true;
|
||||
|
||||
/* Reset and Start SMU logging - to monitor the s0i3 stats */
|
||||
amd_pmc_setup_smu_logging(pdev);
|
||||
|
||||
|
|
@ -617,8 +763,7 @@ static void amd_pmc_s2idle_prepare(void)
|
|||
}
|
||||
}
|
||||
|
||||
msg = amd_pmc_get_os_hint(pdev);
|
||||
rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false);
|
||||
rc = amd_pmc_send_cmd(pdev, arg, NULL, pdev->cpu_info->os_hint, false);
|
||||
if (rc) {
|
||||
dev_err(pdev->dev, "suspend failed: %d\n", rc);
|
||||
return;
|
||||
|
|
@ -632,11 +777,10 @@ static void amd_pmc_s2idle_prepare(void)
|
|||
static void amd_pmc_s2idle_check(void)
|
||||
{
|
||||
struct amd_pmc_dev *pdev = &pmc;
|
||||
struct smu_metrics table;
|
||||
int rc;
|
||||
|
||||
/* Avoid triggering OVP */
|
||||
if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status)
|
||||
if (amd_pmc_intermediate_wakeup_need_delay(pdev) ||
|
||||
amd_pmc_want_suspend_delay(pdev))
|
||||
msleep(2500);
|
||||
|
||||
/* Dump the IdleMask before we add to the STB */
|
||||
|
|
@ -645,6 +789,9 @@ static void amd_pmc_s2idle_check(void)
|
|||
rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK);
|
||||
if (rc)
|
||||
dev_err(pdev->dev, "error writing to STB: %d\n", rc);
|
||||
|
||||
/* remember that first check after suspend is done (until next prepare) */
|
||||
pdev->is_first_check_after_suspend = false;
|
||||
}
|
||||
|
||||
static int amd_pmc_dump_data(struct amd_pmc_dev *pdev)
|
||||
|
|
@ -659,10 +806,8 @@ static void amd_pmc_s2idle_restore(void)
|
|||
{
|
||||
struct amd_pmc_dev *pdev = &pmc;
|
||||
int rc;
|
||||
u8 msg;
|
||||
|
||||
msg = amd_pmc_get_os_hint(pdev);
|
||||
rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false);
|
||||
rc = amd_pmc_send_cmd(pdev, 0, NULL, pdev->cpu_info->os_hint, false);
|
||||
if (rc)
|
||||
dev_err(pdev->dev, "resume failed: %d\n", rc);
|
||||
|
||||
|
|
@ -709,22 +854,6 @@ static const struct dev_pm_ops amd_pmc_pm = {
|
|||
.suspend = amd_pmc_suspend_handler,
|
||||
};
|
||||
|
||||
static const struct pci_device_id pmc_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SHP) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_VG) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int amd_pmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct amd_pmc_dev *dev = &pmc;
|
||||
|
|
@ -736,17 +865,14 @@ static int amd_pmc_probe(struct platform_device *pdev)
|
|||
|
||||
dev->dev = &pdev->dev;
|
||||
rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
|
||||
if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
|
||||
if (!rdev) {
|
||||
err = -ENODEV;
|
||||
goto err_pci_dev_put;
|
||||
}
|
||||
|
||||
dev->cpu_id = rdev->device;
|
||||
if (dev->cpu_id == AMD_CPU_ID_SP || dev->cpu_id == AMD_CPU_ID_SHP) {
|
||||
dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
|
||||
err = -ENODEV;
|
||||
err = amd_pmc_set_cpu_info(dev, rdev);
|
||||
if (err)
|
||||
goto err_pci_dev_put;
|
||||
}
|
||||
|
||||
dev->rdev = rdev;
|
||||
err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val);
|
||||
|
|
@ -778,9 +904,6 @@ static int amd_pmc_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto err_pci_dev_put;
|
||||
|
||||
/* Get num of IP blocks within the SoC */
|
||||
amd_pmc_get_ip_info(dev);
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
if (IS_ENABLED(CONFIG_SUSPEND)) {
|
||||
err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops);
|
||||
|
|
@ -825,6 +948,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = {
|
|||
{"AMDI0009", 0},
|
||||
{"AMDI000A", 0},
|
||||
{"AMDI000B", 0},
|
||||
{"AMDI000C", 0},
|
||||
{"AMD0004", 0},
|
||||
{"AMD0005", 0},
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@
|
|||
/* SMU communication registers */
|
||||
#define AMD_PMC_REGISTER_RESPONSE 0x980
|
||||
#define AMD_PMC_REGISTER_ARGUMENT 0x9BC
|
||||
#define AMD_PMC_REGISTER_MESSAGE 0x538
|
||||
|
||||
/* SMU communication registers for 1Ah 20h SoC */
|
||||
#define AMD_PMC_REGISTER_MSG_1AH_20H 0x938
|
||||
|
||||
/* SMU communication registers for 1Ah 80h SoC */
|
||||
#define AMD_PMC_REGISTER_MSG_1AH_80H 0xA10
|
||||
#define AMD_PMC_REGISTER_ARG_1AH_80H 0xA18
|
||||
#define AMD_PMC_REGISTER_RSP_1AH_80H 0xA14
|
||||
|
||||
/* PMC Scratch Registers */
|
||||
#define AMD_PMC_SCRATCH_REG_CZN 0x94
|
||||
|
|
@ -90,6 +99,22 @@ struct stb_arg {
|
|||
u32 resp;
|
||||
};
|
||||
|
||||
struct amd_pmc_bit_map {
|
||||
const char *name;
|
||||
u32 bit_mask;
|
||||
};
|
||||
|
||||
/* SoC-specific information */
|
||||
struct amd_pmc_cpu_info {
|
||||
u32 smu_msg;
|
||||
u32 smu_arg;
|
||||
u32 smu_rsp;
|
||||
u32 num_ips;
|
||||
u32 scratch_reg;
|
||||
const struct amd_pmc_bit_map *ips_ptr;
|
||||
u8 os_hint;
|
||||
};
|
||||
|
||||
struct amd_pmc_dev {
|
||||
void __iomem *regbase;
|
||||
void __iomem *smu_virt_addr;
|
||||
|
|
@ -99,9 +124,6 @@ struct amd_pmc_dev {
|
|||
u32 cpu_id;
|
||||
u32 dram_size;
|
||||
u32 active_ips;
|
||||
const struct amd_pmc_bit_map *ips_ptr;
|
||||
u32 num_ips;
|
||||
u32 smu_msg;
|
||||
/* SMU version information */
|
||||
u8 smu_program;
|
||||
u8 major;
|
||||
|
|
@ -114,13 +136,10 @@ struct amd_pmc_dev {
|
|||
struct dentry *dbgfs_dir;
|
||||
struct quirk_entry *quirks;
|
||||
bool disable_8042_wakeup;
|
||||
bool is_first_check_after_suspend;
|
||||
struct amd_mp2_dev *mp2;
|
||||
struct stb_arg stb_arg;
|
||||
};
|
||||
|
||||
struct amd_pmc_bit_map {
|
||||
const char *name;
|
||||
u32 bit_mask;
|
||||
const struct amd_pmc_cpu_info *cpu_info;
|
||||
};
|
||||
|
||||
struct smu_metrics {
|
||||
|
|
@ -147,24 +166,35 @@ enum amd_pmc_def {
|
|||
};
|
||||
|
||||
void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev);
|
||||
bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev);
|
||||
void amd_pmc_quirks_init(struct amd_pmc_dev *dev);
|
||||
void amd_mp2_stb_init(struct amd_pmc_dev *dev);
|
||||
void amd_mp2_stb_deinit(struct amd_pmc_dev *dev);
|
||||
|
||||
/* List of supported CPU ids */
|
||||
#define AMD_CPU_ID_RV 0x15D0
|
||||
#define AMD_CPU_ID_RN 0x1630
|
||||
#define AMD_CPU_ID_PCO AMD_CPU_ID_RV
|
||||
#define AMD_CPU_ID_CZN AMD_CPU_ID_RN
|
||||
#define AMD_CPU_ID_VG 0x1645
|
||||
#define AMD_CPU_ID_YC 0x14B5
|
||||
#define AMD_CPU_ID_CB 0x14D8
|
||||
#define AMD_CPU_ID_PS 0x14E8
|
||||
#define AMD_CPU_ID_SP 0x14A4
|
||||
#define AMD_CPU_ID_SHP 0x153A
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
|
||||
#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c
|
||||
/* List of supported CPU/device IDs */
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_PCO 0x15D0
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_CZN 0x1630
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_VG 0x1645
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_YC 0x14B5
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_CB 0x14D8
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_PS 0x14E8
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_SP 0x14A4
|
||||
#define PCI_DEVICE_ID_AMD_CPU_ID_SHP 0x153A
|
||||
|
||||
/* Backward compatibility aliases */
|
||||
#define AMD_CPU_ID_PCO PCI_DEVICE_ID_AMD_CPU_ID_PCO
|
||||
#define AMD_CPU_ID_CZN PCI_DEVICE_ID_AMD_CPU_ID_CZN
|
||||
#define AMD_CPU_ID_VG PCI_DEVICE_ID_AMD_CPU_ID_VG
|
||||
#define AMD_CPU_ID_YC PCI_DEVICE_ID_AMD_CPU_ID_YC
|
||||
#define AMD_CPU_ID_CB PCI_DEVICE_ID_AMD_CPU_ID_CB
|
||||
#define AMD_CPU_ID_PS PCI_DEVICE_ID_AMD_CPU_ID_PS
|
||||
#define AMD_CPU_ID_SP PCI_DEVICE_ID_AMD_CPU_ID_SP
|
||||
#define AMD_CPU_ID_SHP PCI_DEVICE_ID_AMD_CPU_ID_SHP
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M80H_ROOT 0x115b
|
||||
#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);
|
||||
|
|
|
|||
|
|
@ -1013,8 +1013,8 @@ static void gmux_remove(struct pnp_dev *pnp)
|
|||
}
|
||||
|
||||
static const struct pnp_device_id gmux_device_ids[] = {
|
||||
{GMUX_ACPI_HID, 0},
|
||||
{"", 0}
|
||||
{ .id = GMUX_ACPI_HID },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops gmux_dev_pm_ops = {
|
||||
|
|
|
|||
|
|
@ -936,6 +936,34 @@ static const struct dmi_system_id power_limits[] = {
|
|||
.requires_fan_curve = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "FX608JPR"),
|
||||
},
|
||||
.driver_data = &(struct power_data) {
|
||||
.ac_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 28,
|
||||
.ppt_pl1_spl_max = 100,
|
||||
.ppt_pl2_sppt_min = 28,
|
||||
.ppt_pl2_sppt_max = 135,
|
||||
.nv_dynamic_boost_min = 10,
|
||||
.nv_dynamic_boost_max = 15,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
.nv_tgp_min = 55,
|
||||
.nv_tgp_max = 100,
|
||||
},
|
||||
.dc_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 25,
|
||||
.ppt_pl1_spl_max = 53,
|
||||
.ppt_pl2_sppt_min = 25,
|
||||
.ppt_pl2_sppt_max = 65,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
},
|
||||
.requires_fan_curve = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GA401Q"),
|
||||
|
|
@ -950,6 +978,38 @@ static const struct dmi_system_id power_limits[] = {
|
|||
.dc_data = NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GA402NJ"),
|
||||
},
|
||||
.driver_data = &(struct power_data) {
|
||||
.ac_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 15,
|
||||
.ppt_pl1_spl_def = 35,
|
||||
.ppt_pl1_spl_max = 80,
|
||||
.ppt_pl2_sppt_min = 25,
|
||||
.ppt_pl2_sppt_def = 65,
|
||||
.ppt_pl2_sppt_max = 80,
|
||||
.ppt_pl3_fppt_min = 35,
|
||||
.ppt_pl3_fppt_max = 80,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
.nv_dynamic_boost_min = 5,
|
||||
.nv_dynamic_boost_def = 10,
|
||||
.nv_dynamic_boost_max = 15,
|
||||
},
|
||||
.dc_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 15,
|
||||
.ppt_pl1_spl_max = 35,
|
||||
.ppt_pl2_sppt_min = 25,
|
||||
.ppt_pl2_sppt_max = 35,
|
||||
.ppt_pl3_fppt_min = 35,
|
||||
.ppt_pl3_fppt_max = 65,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
// This model is full AMD. No Nvidia dGPU.
|
||||
|
|
@ -1033,6 +1093,38 @@ static const struct dmi_system_id power_limits[] = {
|
|||
.requires_fan_curve = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GA403UM"),
|
||||
},
|
||||
.driver_data = &(struct power_data) {
|
||||
.ac_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 15,
|
||||
.ppt_pl1_spl_max = 80,
|
||||
.ppt_pl2_sppt_min = 25,
|
||||
.ppt_pl2_sppt_max = 80,
|
||||
.ppt_pl3_fppt_min = 35,
|
||||
.ppt_pl3_fppt_max = 80,
|
||||
.nv_dynamic_boost_min = 0,
|
||||
.nv_dynamic_boost_max = 15,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
.nv_tgp_min = 55,
|
||||
.nv_tgp_max = 85,
|
||||
},
|
||||
.dc_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 15,
|
||||
.ppt_pl1_spl_max = 35,
|
||||
.ppt_pl2_sppt_min = 25,
|
||||
.ppt_pl2_sppt_max = 35,
|
||||
.ppt_pl3_fppt_min = 35,
|
||||
.ppt_pl3_fppt_max = 65,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
},
|
||||
.requires_fan_curve = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GA403UV"),
|
||||
|
|
@ -1898,6 +1990,40 @@ static const struct dmi_system_id power_limits[] = {
|
|||
.requires_fan_curve = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "G614PR"),
|
||||
},
|
||||
.driver_data = &(struct power_data) {
|
||||
.ac_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 30,
|
||||
.ppt_pl1_spl_max = 90,
|
||||
.ppt_pl2_sppt_min = 65,
|
||||
.ppt_pl2_sppt_def = 110,
|
||||
.ppt_pl2_sppt_max = 125,
|
||||
.ppt_pl3_fppt_min = 65,
|
||||
.ppt_pl3_fppt_def = 110,
|
||||
.ppt_pl3_fppt_max = 125,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
.nv_dynamic_boost_min = 5,
|
||||
.nv_dynamic_boost_max = 25,
|
||||
.nv_tgp_min = 65,
|
||||
.nv_tgp_max = 115,
|
||||
},
|
||||
.dc_data = &(struct power_limits) {
|
||||
.ppt_pl1_spl_min = 25,
|
||||
.ppt_pl1_spl_max = 65,
|
||||
.ppt_pl2_sppt_min = 25,
|
||||
.ppt_pl2_sppt_max = 65,
|
||||
.ppt_pl3_fppt_min = 35,
|
||||
.ppt_pl3_fppt_max = 75,
|
||||
.nv_temp_target_min = 75,
|
||||
.nv_temp_target_max = 87,
|
||||
},
|
||||
.requires_fan_curve = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "G615LR"),
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ module_param(fnlock_default, bool, 0444);
|
|||
#define NOTIFY_KBD_TTP 0xae
|
||||
#define NOTIFY_LID_FLIP 0xfa
|
||||
#define NOTIFY_LID_FLIP_ROG 0xbd
|
||||
#define NOTIFY_KEYSTONE 0xb4
|
||||
|
||||
#define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0)
|
||||
|
||||
|
|
@ -279,6 +280,8 @@ struct asus_wmi {
|
|||
u32 tablet_switch_dev_id;
|
||||
bool tablet_switch_inverted;
|
||||
|
||||
bool keystone_available;
|
||||
|
||||
enum fan_type fan_type;
|
||||
enum fan_type gpu_fan_type;
|
||||
enum fan_type mid_fan_type;
|
||||
|
|
@ -646,6 +649,55 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
|
|||
return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
|
||||
}
|
||||
|
||||
/* Keystone *******************************************************************/
|
||||
|
||||
static int keystone_check_present(struct asus_wmi *asus)
|
||||
{
|
||||
u32 retval;
|
||||
int err;
|
||||
|
||||
asus->keystone_available = false;
|
||||
|
||||
/*
|
||||
* Use a raw devstate call rather than asus_wmi_dev_is_present().
|
||||
* For this devid, PRESENCE_BIT encodes current insert state, not
|
||||
* feature presence, so asus_wmi_dev_is_present() would return false
|
||||
* whenever the dongle is absent at boot, even on machines that have
|
||||
* a keystone slot.
|
||||
* -ENODEV means the firmware doesn't know this devid at all.
|
||||
* retval is not examined here, only the return code matters.
|
||||
*/
|
||||
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_KEYSTONE, &retval);
|
||||
if (err == -ENODEV)
|
||||
return 0;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
asus->keystone_available = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t keystone_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct asus_wmi *asus = dev_get_drvdata(dev);
|
||||
u32 retval;
|
||||
int err;
|
||||
|
||||
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_KEYSTONE, &retval);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !!(retval & ASUS_WMI_DSTS_PRESENCE_BIT));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(keystone);
|
||||
|
||||
static void asus_wmi_keystone_notify(struct asus_wmi *asus)
|
||||
{
|
||||
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "keystone");
|
||||
}
|
||||
|
||||
/* Input **********************************************************************/
|
||||
static void asus_wmi_tablet_sw_report(struct asus_wmi *asus, bool value)
|
||||
{
|
||||
|
|
@ -4575,6 +4627,12 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
|||
return;
|
||||
}
|
||||
|
||||
if (code == NOTIFY_KEYSTONE) {
|
||||
if (asus->keystone_available)
|
||||
asus_wmi_keystone_notify(asus);
|
||||
return;
|
||||
}
|
||||
|
||||
if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) {
|
||||
if (asus->fan_boost_mode_available)
|
||||
fan_boost_mode_switch_next(asus);
|
||||
|
|
@ -4698,6 +4756,7 @@ static struct attribute *platform_attributes[] = {
|
|||
&dev_attr_lid_resume.attr,
|
||||
&dev_attr_als_enable.attr,
|
||||
&dev_attr_fan_boost_mode.attr,
|
||||
&dev_attr_keystone.attr,
|
||||
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
||||
&dev_attr_charge_mode.attr,
|
||||
&dev_attr_egpu_enable.attr,
|
||||
|
|
@ -4741,6 +4800,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
|||
devid = ASUS_WMI_DEVID_ALS_ENABLE;
|
||||
else if (attr == &dev_attr_fan_boost_mode.attr)
|
||||
ok = asus->fan_boost_mode_available;
|
||||
else if (attr == &dev_attr_keystone.attr)
|
||||
ok = asus->keystone_available;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
||||
if (attr == &dev_attr_charge_mode.attr)
|
||||
|
|
@ -5081,6 +5142,10 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto fail_platform_profile_setup;
|
||||
|
||||
err = keystone_check_present(asus);
|
||||
if (err)
|
||||
dev_warn(&pdev->dev, "Failed to check Keystone presence: %d\n", err);
|
||||
|
||||
err = asus_wmi_sysfs_init(asus->platform_device);
|
||||
if (err)
|
||||
goto fail_sysfs;
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ static const struct software_node vendor_key_node = {
|
|||
};
|
||||
|
||||
static const struct software_node *p50_swnodes[] = {
|
||||
&gpiochip_node,
|
||||
&gpio_leds_node,
|
||||
&identify_led_node,
|
||||
&gpio_keys_node,
|
||||
|
|
@ -424,6 +423,13 @@ MODULE_DEVICE_TABLE(dmi, dmi_ids);
|
|||
static int __init p50_module_init(void)
|
||||
{
|
||||
struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
|
||||
struct platform_device_info pdevinfo = {
|
||||
.name = DRIVER_NAME,
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
.res = &res,
|
||||
.num_res = 1,
|
||||
.swnode = &gpiochip_node,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!dmi_first_match(dmi_ids))
|
||||
|
|
@ -433,7 +439,7 @@ static int __init p50_module_init(void)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
|
||||
gpio_pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(gpio_pdev)) {
|
||||
pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
|
||||
platform_driver_unregister(&p50_gpio_driver);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/input.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct cmpc_accel {
|
||||
int sensitivity;
|
||||
|
|
@ -38,8 +39,8 @@ struct cmpc_accel {
|
|||
|
||||
typedef void (*input_device_init)(struct input_dev *dev);
|
||||
|
||||
static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
|
||||
input_device_init idev_init)
|
||||
static int cmpc_add_notify_device(struct device *dev, char *name,
|
||||
input_device_init idev_init)
|
||||
{
|
||||
struct input_dev *inputdev;
|
||||
int error;
|
||||
|
|
@ -48,22 +49,20 @@ static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
|
|||
if (!inputdev)
|
||||
return -ENOMEM;
|
||||
inputdev->name = name;
|
||||
inputdev->dev.parent = &acpi->dev;
|
||||
inputdev->dev.parent = dev;
|
||||
idev_init(inputdev);
|
||||
error = input_register_device(inputdev);
|
||||
if (error) {
|
||||
input_free_device(inputdev);
|
||||
return error;
|
||||
}
|
||||
dev_set_drvdata(&acpi->dev, inputdev);
|
||||
dev_set_drvdata(dev, inputdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
|
||||
static void cmpc_remove_notify_device(struct device *dev)
|
||||
{
|
||||
struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
|
||||
input_unregister_device(inputdev);
|
||||
return 0;
|
||||
input_unregister_device(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -179,15 +178,17 @@ static acpi_status cmpc_get_accel_v4(acpi_handle handle,
|
|||
return status;
|
||||
}
|
||||
|
||||
static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
|
||||
static void cmpc_accel_handler_v4(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
if (event == 0x81) {
|
||||
int16_t x, y, z;
|
||||
acpi_status status;
|
||||
|
||||
status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
|
||||
status = cmpc_get_accel_v4(ACPI_HANDLE(dev), &x, &y, &z);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
|
||||
struct input_dev *inputdev = dev_get_drvdata(dev);
|
||||
|
||||
input_report_abs(inputdev, ABS_X, x);
|
||||
input_report_abs(inputdev, ABS_Y, y);
|
||||
|
|
@ -317,18 +318,17 @@ static struct device_attribute cmpc_accel_g_select_attr_v4 = {
|
|||
|
||||
static int cmpc_accel_open_v4(struct input_dev *input)
|
||||
{
|
||||
struct acpi_device *acpi;
|
||||
acpi_handle handle = ACPI_HANDLE(input->dev.parent);
|
||||
struct cmpc_accel *accel;
|
||||
|
||||
acpi = to_acpi_device(input->dev.parent);
|
||||
accel = dev_get_drvdata(&input->dev);
|
||||
if (!accel)
|
||||
return -ENXIO;
|
||||
|
||||
cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
|
||||
cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
|
||||
cmpc_accel_set_sensitivity_v4(handle, accel->sensitivity);
|
||||
cmpc_accel_set_g_select_v4(handle, accel->g_select);
|
||||
|
||||
if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
|
||||
if (ACPI_SUCCESS(cmpc_start_accel_v4(handle))) {
|
||||
accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -337,13 +337,11 @@ static int cmpc_accel_open_v4(struct input_dev *input)
|
|||
|
||||
static void cmpc_accel_close_v4(struct input_dev *input)
|
||||
{
|
||||
struct acpi_device *acpi;
|
||||
struct cmpc_accel *accel;
|
||||
|
||||
acpi = to_acpi_device(input->dev.parent);
|
||||
accel = dev_get_drvdata(&input->dev);
|
||||
|
||||
cmpc_stop_accel_v4(acpi->handle);
|
||||
cmpc_stop_accel_v4(ACPI_HANDLE(input->dev.parent));
|
||||
accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
|
||||
}
|
||||
|
||||
|
|
@ -367,7 +365,7 @@ static int cmpc_accel_suspend_v4(struct device *dev)
|
|||
accel = dev_get_drvdata(&inputdev->dev);
|
||||
|
||||
if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
|
||||
return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
|
||||
return cmpc_stop_accel_v4(ACPI_HANDLE(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -381,12 +379,12 @@ static int cmpc_accel_resume_v4(struct device *dev)
|
|||
accel = dev_get_drvdata(&inputdev->dev);
|
||||
|
||||
if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
|
||||
cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
|
||||
accel->sensitivity);
|
||||
cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
|
||||
accel->g_select);
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
|
||||
if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
|
||||
cmpc_accel_set_sensitivity_v4(handle, accel->sensitivity);
|
||||
cmpc_accel_set_g_select_v4(handle, accel->g_select);
|
||||
|
||||
if (ACPI_FAILURE(cmpc_start_accel_v4(handle)))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
@ -394,18 +392,30 @@ static int cmpc_accel_resume_v4(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int cmpc_accel_add_v4(struct acpi_device *acpi)
|
||||
static int cmpc_accel_probe_v4(struct platform_device *pdev)
|
||||
{
|
||||
int error;
|
||||
struct input_dev *inputdev;
|
||||
struct cmpc_accel *accel;
|
||||
struct acpi_device *acpi;
|
||||
|
||||
accel = kmalloc_obj(*accel);
|
||||
acpi = ACPI_COMPANION(&pdev->dev);
|
||||
if (!acpi)
|
||||
return -ENODEV;
|
||||
|
||||
accel = devm_kzalloc(&pdev->dev, sizeof(*accel), GFP_KERNEL);
|
||||
if (!accel)
|
||||
return -ENOMEM;
|
||||
|
||||
accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
|
||||
|
||||
error = cmpc_add_notify_device(&pdev->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
inputdev = dev_get_drvdata(&pdev->dev);
|
||||
dev_set_drvdata(&acpi->dev, inputdev);
|
||||
|
||||
accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
|
||||
cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
|
||||
|
||||
|
|
@ -420,30 +430,35 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi)
|
|||
if (error)
|
||||
goto failed_g_select;
|
||||
|
||||
error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
|
||||
cmpc_accel_idev_init_v4);
|
||||
error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY,
|
||||
cmpc_accel_handler_v4, &pdev->dev);
|
||||
if (error)
|
||||
goto failed_input;
|
||||
goto failed_notify_handler;
|
||||
|
||||
inputdev = dev_get_drvdata(&acpi->dev);
|
||||
dev_set_drvdata(&inputdev->dev, accel);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_input:
|
||||
failed_notify_handler:
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
|
||||
failed_g_select:
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
|
||||
failed_sensitivity:
|
||||
kfree(accel);
|
||||
dev_set_drvdata(&acpi->dev, NULL);
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void cmpc_accel_remove_v4(struct acpi_device *acpi)
|
||||
static void cmpc_accel_remove_v4(struct platform_device *pdev)
|
||||
{
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
|
||||
struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev);
|
||||
|
||||
acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY,
|
||||
cmpc_accel_handler_v4);
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
|
||||
cmpc_remove_acpi_notify_device(acpi);
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
|
||||
dev_set_drvdata(&acpi->dev, NULL);
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
|
||||
|
|
@ -454,16 +469,14 @@ static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
|
|||
{"", 0}
|
||||
};
|
||||
|
||||
static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
|
||||
.name = "cmpc_accel_v4",
|
||||
.class = "cmpc_accel_v4",
|
||||
.ids = cmpc_accel_device_ids_v4,
|
||||
.ops = {
|
||||
.add = cmpc_accel_add_v4,
|
||||
.remove = cmpc_accel_remove_v4,
|
||||
.notify = cmpc_accel_handler_v4,
|
||||
static struct platform_driver cmpc_accel_acpi_driver_v4 = {
|
||||
.probe = cmpc_accel_probe_v4,
|
||||
.remove = cmpc_accel_remove_v4,
|
||||
.driver = {
|
||||
.name = "cmpc_accel_v4",
|
||||
.acpi_match_table = cmpc_accel_device_ids_v4,
|
||||
.pm = &cmpc_accel_pm,
|
||||
},
|
||||
.drv.pm = &cmpc_accel_pm,
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -543,15 +556,17 @@ static acpi_status cmpc_get_accel(acpi_handle handle,
|
|||
return status;
|
||||
}
|
||||
|
||||
static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
|
||||
static void cmpc_accel_handler(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
if (event == 0x81) {
|
||||
unsigned char x, y, z;
|
||||
acpi_status status;
|
||||
|
||||
status = cmpc_get_accel(dev->handle, &x, &y, &z);
|
||||
status = cmpc_get_accel(ACPI_HANDLE(dev), &x, &y, &z);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
|
||||
struct input_dev *inputdev = dev_get_drvdata(dev);
|
||||
|
||||
input_report_abs(inputdev, ABS_X, x);
|
||||
input_report_abs(inputdev, ABS_Y, y);
|
||||
|
|
@ -618,20 +633,14 @@ static struct device_attribute cmpc_accel_sensitivity_attr = {
|
|||
|
||||
static int cmpc_accel_open(struct input_dev *input)
|
||||
{
|
||||
struct acpi_device *acpi;
|
||||
|
||||
acpi = to_acpi_device(input->dev.parent);
|
||||
if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
|
||||
if (ACPI_SUCCESS(cmpc_start_accel(ACPI_HANDLE(input->dev.parent))))
|
||||
return 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void cmpc_accel_close(struct input_dev *input)
|
||||
{
|
||||
struct acpi_device *acpi;
|
||||
|
||||
acpi = to_acpi_device(input->dev.parent);
|
||||
cmpc_stop_accel(acpi->handle);
|
||||
cmpc_stop_accel(ACPI_HANDLE(input->dev.parent));
|
||||
}
|
||||
|
||||
static void cmpc_accel_idev_init(struct input_dev *inputdev)
|
||||
|
|
@ -644,16 +653,28 @@ static void cmpc_accel_idev_init(struct input_dev *inputdev)
|
|||
inputdev->close = cmpc_accel_close;
|
||||
}
|
||||
|
||||
static int cmpc_accel_add(struct acpi_device *acpi)
|
||||
static int cmpc_accel_probe(struct platform_device *pdev)
|
||||
{
|
||||
int error;
|
||||
struct input_dev *inputdev;
|
||||
struct cmpc_accel *accel;
|
||||
struct acpi_device *acpi;
|
||||
|
||||
accel = kmalloc_obj(*accel);
|
||||
acpi = ACPI_COMPANION(&pdev->dev);
|
||||
if (!acpi)
|
||||
return -ENODEV;
|
||||
|
||||
accel = devm_kzalloc(&pdev->dev, sizeof(*accel), GFP_KERNEL);
|
||||
if (!accel)
|
||||
return -ENOMEM;
|
||||
|
||||
error = cmpc_add_notify_device(&pdev->dev, "cmpc_accel", cmpc_accel_idev_init);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
inputdev = dev_get_drvdata(&pdev->dev);
|
||||
dev_set_drvdata(&acpi->dev, inputdev);
|
||||
|
||||
accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
|
||||
cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
|
||||
|
||||
|
|
@ -661,27 +682,32 @@ static int cmpc_accel_add(struct acpi_device *acpi)
|
|||
if (error)
|
||||
goto failed_file;
|
||||
|
||||
error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
|
||||
cmpc_accel_idev_init);
|
||||
error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY,
|
||||
cmpc_accel_handler, &pdev->dev);
|
||||
if (error)
|
||||
goto failed_input;
|
||||
goto failed_notify_handler;
|
||||
|
||||
inputdev = dev_get_drvdata(&acpi->dev);
|
||||
dev_set_drvdata(&inputdev->dev, accel);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_input:
|
||||
failed_notify_handler:
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
|
||||
failed_file:
|
||||
kfree(accel);
|
||||
dev_set_drvdata(&acpi->dev, NULL);
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void cmpc_accel_remove(struct acpi_device *acpi)
|
||||
static void cmpc_accel_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev);
|
||||
|
||||
acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY,
|
||||
cmpc_accel_handler);
|
||||
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
|
||||
cmpc_remove_acpi_notify_device(acpi);
|
||||
dev_set_drvdata(&acpi->dev, NULL);
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id cmpc_accel_device_ids[] = {
|
||||
|
|
@ -689,15 +715,13 @@ static const struct acpi_device_id cmpc_accel_device_ids[] = {
|
|||
{"", 0}
|
||||
};
|
||||
|
||||
static struct acpi_driver cmpc_accel_acpi_driver = {
|
||||
.name = "cmpc_accel",
|
||||
.class = "cmpc_accel",
|
||||
.ids = cmpc_accel_device_ids,
|
||||
.ops = {
|
||||
.add = cmpc_accel_add,
|
||||
.remove = cmpc_accel_remove,
|
||||
.notify = cmpc_accel_handler,
|
||||
}
|
||||
static struct platform_driver cmpc_accel_acpi_driver = {
|
||||
.probe = cmpc_accel_probe,
|
||||
.remove = cmpc_accel_remove,
|
||||
.driver = {
|
||||
.name = "cmpc_accel",
|
||||
.acpi_match_table = cmpc_accel_device_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -722,13 +746,14 @@ static acpi_status cmpc_get_tablet(acpi_handle handle,
|
|||
return status;
|
||||
}
|
||||
|
||||
static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
|
||||
static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
unsigned long long val = 0;
|
||||
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
|
||||
struct input_dev *inputdev = dev_get_drvdata(dev);
|
||||
|
||||
if (event == 0x81) {
|
||||
if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
|
||||
if (ACPI_SUCCESS(cmpc_get_tablet(ACPI_HANDLE(dev), &val))) {
|
||||
input_report_switch(inputdev, SW_TABLET_MODE, !val);
|
||||
input_sync(inputdev);
|
||||
}
|
||||
|
|
@ -737,28 +762,44 @@ static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
|
|||
|
||||
static void cmpc_tablet_idev_init(struct input_dev *inputdev)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(inputdev->dev.parent);
|
||||
unsigned long long val = 0;
|
||||
struct acpi_device *acpi;
|
||||
|
||||
set_bit(EV_SW, inputdev->evbit);
|
||||
set_bit(SW_TABLET_MODE, inputdev->swbit);
|
||||
|
||||
acpi = to_acpi_device(inputdev->dev.parent);
|
||||
if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
|
||||
if (ACPI_SUCCESS(cmpc_get_tablet(handle, &val))) {
|
||||
input_report_switch(inputdev, SW_TABLET_MODE, !val);
|
||||
input_sync(inputdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int cmpc_tablet_add(struct acpi_device *acpi)
|
||||
static int cmpc_tablet_probe(struct platform_device *pdev)
|
||||
{
|
||||
return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
|
||||
cmpc_tablet_idev_init);
|
||||
struct acpi_device *acpi;
|
||||
int error;
|
||||
|
||||
acpi = ACPI_COMPANION(&pdev->dev);
|
||||
if (!acpi)
|
||||
return -ENODEV;
|
||||
|
||||
error = cmpc_add_notify_device(&pdev->dev, "cmpc_tablet", cmpc_tablet_idev_init);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY,
|
||||
cmpc_tablet_handler, &pdev->dev);
|
||||
if (error)
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void cmpc_tablet_remove(struct acpi_device *acpi)
|
||||
static void cmpc_tablet_remove(struct platform_device *pdev)
|
||||
{
|
||||
cmpc_remove_acpi_notify_device(acpi);
|
||||
acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev),
|
||||
ACPI_DEVICE_NOTIFY, cmpc_tablet_handler);
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
@ -767,7 +808,7 @@ static int cmpc_tablet_resume(struct device *dev)
|
|||
struct input_dev *inputdev = dev_get_drvdata(dev);
|
||||
|
||||
unsigned long long val = 0;
|
||||
if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
|
||||
if (ACPI_SUCCESS(cmpc_get_tablet(ACPI_HANDLE(dev), &val))) {
|
||||
input_report_switch(inputdev, SW_TABLET_MODE, !val);
|
||||
input_sync(inputdev);
|
||||
}
|
||||
|
|
@ -782,16 +823,14 @@ static const struct acpi_device_id cmpc_tablet_device_ids[] = {
|
|||
{"", 0}
|
||||
};
|
||||
|
||||
static struct acpi_driver cmpc_tablet_acpi_driver = {
|
||||
.name = "cmpc_tablet",
|
||||
.class = "cmpc_tablet",
|
||||
.ids = cmpc_tablet_device_ids,
|
||||
.ops = {
|
||||
.add = cmpc_tablet_add,
|
||||
.remove = cmpc_tablet_remove,
|
||||
.notify = cmpc_tablet_handler,
|
||||
static struct platform_driver cmpc_tablet_acpi_driver = {
|
||||
.probe = cmpc_tablet_probe,
|
||||
.remove = cmpc_tablet_remove,
|
||||
.driver = {
|
||||
.name = "cmpc_tablet",
|
||||
.acpi_match_table = cmpc_tablet_device_ids,
|
||||
.pm = &cmpc_tablet_pm,
|
||||
},
|
||||
.drv.pm = &cmpc_tablet_pm,
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -958,11 +997,16 @@ struct ipml200_dev {
|
|||
struct rfkill *rf;
|
||||
};
|
||||
|
||||
static int cmpc_ipml_add(struct acpi_device *acpi)
|
||||
static int cmpc_ipml_probe(struct platform_device *pdev)
|
||||
{
|
||||
int retval;
|
||||
struct ipml200_dev *ipml;
|
||||
struct backlight_properties props;
|
||||
acpi_handle handle;
|
||||
|
||||
handle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
ipml = kmalloc_obj(*ipml);
|
||||
if (ipml == NULL)
|
||||
|
|
@ -971,16 +1015,16 @@ static int cmpc_ipml_add(struct acpi_device *acpi)
|
|||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.max_brightness = 7;
|
||||
ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
|
||||
acpi->handle, &cmpc_bl_ops,
|
||||
ipml->bd = backlight_device_register("cmpc_bl", &pdev->dev,
|
||||
handle, &cmpc_bl_ops,
|
||||
&props);
|
||||
if (IS_ERR(ipml->bd)) {
|
||||
retval = PTR_ERR(ipml->bd);
|
||||
goto out_bd;
|
||||
}
|
||||
|
||||
ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
|
||||
&cmpc_rfkill_ops, acpi->handle);
|
||||
ipml->rf = rfkill_alloc("cmpc_rfkill", &pdev->dev, RFKILL_TYPE_WLAN,
|
||||
&cmpc_rfkill_ops, handle);
|
||||
/*
|
||||
* If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
|
||||
* This is OK, however, since all other uses of the device will not
|
||||
|
|
@ -994,7 +1038,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi)
|
|||
}
|
||||
}
|
||||
|
||||
dev_set_drvdata(&acpi->dev, ipml);
|
||||
platform_set_drvdata(pdev, ipml);
|
||||
return 0;
|
||||
|
||||
out_bd:
|
||||
|
|
@ -1002,11 +1046,11 @@ static int cmpc_ipml_add(struct acpi_device *acpi)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void cmpc_ipml_remove(struct acpi_device *acpi)
|
||||
static void cmpc_ipml_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ipml200_dev *ipml;
|
||||
|
||||
ipml = dev_get_drvdata(&acpi->dev);
|
||||
ipml = platform_get_drvdata(pdev);
|
||||
|
||||
backlight_device_unregister(ipml->bd);
|
||||
|
||||
|
|
@ -1023,14 +1067,13 @@ static const struct acpi_device_id cmpc_ipml_device_ids[] = {
|
|||
{"", 0}
|
||||
};
|
||||
|
||||
static struct acpi_driver cmpc_ipml_acpi_driver = {
|
||||
.name = "cmpc",
|
||||
.class = "cmpc",
|
||||
.ids = cmpc_ipml_device_ids,
|
||||
.ops = {
|
||||
.add = cmpc_ipml_add,
|
||||
.remove = cmpc_ipml_remove
|
||||
}
|
||||
static struct platform_driver cmpc_ipml_acpi_driver = {
|
||||
.probe = cmpc_ipml_probe,
|
||||
.remove = cmpc_ipml_remove,
|
||||
.driver = {
|
||||
.name = "cmpc",
|
||||
.acpi_match_table = cmpc_ipml_device_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1053,14 +1096,15 @@ static int cmpc_keys_codes[] = {
|
|||
KEY_MAX
|
||||
};
|
||||
|
||||
static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
|
||||
static void cmpc_keys_handler(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
struct input_dev *inputdev;
|
||||
int code = KEY_MAX;
|
||||
|
||||
if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
|
||||
code = cmpc_keys_codes[event & 0x0F];
|
||||
inputdev = dev_get_drvdata(&dev->dev);
|
||||
inputdev = dev_get_drvdata(dev);
|
||||
input_report_key(inputdev, code, !(event & 0x10));
|
||||
input_sync(inputdev);
|
||||
}
|
||||
|
|
@ -1074,15 +1118,32 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev)
|
|||
set_bit(cmpc_keys_codes[i], inputdev->keybit);
|
||||
}
|
||||
|
||||
static int cmpc_keys_add(struct acpi_device *acpi)
|
||||
static int cmpc_keys_probe(struct platform_device *pdev)
|
||||
{
|
||||
return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
|
||||
cmpc_keys_idev_init);
|
||||
struct acpi_device *acpi;
|
||||
int error;
|
||||
|
||||
acpi = ACPI_COMPANION(&pdev->dev);
|
||||
if (!acpi)
|
||||
return -ENODEV;
|
||||
|
||||
error = cmpc_add_notify_device(&pdev->dev, "cmpc_keys", cmpc_keys_idev_init);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY,
|
||||
cmpc_keys_handler, &pdev->dev);
|
||||
if (error)
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void cmpc_keys_remove(struct acpi_device *acpi)
|
||||
static void cmpc_keys_remove(struct platform_device *pdev)
|
||||
{
|
||||
cmpc_remove_acpi_notify_device(acpi);
|
||||
acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev),
|
||||
ACPI_DEVICE_NOTIFY, cmpc_keys_handler);
|
||||
cmpc_remove_notify_device(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id cmpc_keys_device_ids[] = {
|
||||
|
|
@ -1090,15 +1151,13 @@ static const struct acpi_device_id cmpc_keys_device_ids[] = {
|
|||
{"", 0}
|
||||
};
|
||||
|
||||
static struct acpi_driver cmpc_keys_acpi_driver = {
|
||||
.name = "cmpc_keys",
|
||||
.class = "cmpc_keys",
|
||||
.ids = cmpc_keys_device_ids,
|
||||
.ops = {
|
||||
.add = cmpc_keys_add,
|
||||
.remove = cmpc_keys_remove,
|
||||
.notify = cmpc_keys_handler,
|
||||
}
|
||||
static struct platform_driver cmpc_keys_acpi_driver = {
|
||||
.probe = cmpc_keys_probe,
|
||||
.remove = cmpc_keys_remove,
|
||||
.driver = {
|
||||
.name = "cmpc_keys",
|
||||
.acpi_match_table = cmpc_keys_device_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1110,39 +1169,39 @@ static int cmpc_init(void)
|
|||
{
|
||||
int r;
|
||||
|
||||
r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
|
||||
r = platform_driver_register(&cmpc_keys_acpi_driver);
|
||||
if (r)
|
||||
goto failed_keys;
|
||||
|
||||
r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
|
||||
r = platform_driver_register(&cmpc_ipml_acpi_driver);
|
||||
if (r)
|
||||
goto failed_bl;
|
||||
|
||||
r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
|
||||
r = platform_driver_register(&cmpc_tablet_acpi_driver);
|
||||
if (r)
|
||||
goto failed_tablet;
|
||||
|
||||
r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
|
||||
r = platform_driver_register(&cmpc_accel_acpi_driver);
|
||||
if (r)
|
||||
goto failed_accel;
|
||||
|
||||
r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
|
||||
r = platform_driver_register(&cmpc_accel_acpi_driver_v4);
|
||||
if (r)
|
||||
goto failed_accel_v4;
|
||||
|
||||
return r;
|
||||
|
||||
failed_accel_v4:
|
||||
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_accel_acpi_driver);
|
||||
|
||||
failed_accel:
|
||||
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_tablet_acpi_driver);
|
||||
|
||||
failed_tablet:
|
||||
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_ipml_acpi_driver);
|
||||
|
||||
failed_bl:
|
||||
acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_keys_acpi_driver);
|
||||
|
||||
failed_keys:
|
||||
return r;
|
||||
|
|
@ -1150,11 +1209,11 @@ static int cmpc_init(void)
|
|||
|
||||
static void cmpc_exit(void)
|
||||
{
|
||||
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
|
||||
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
|
||||
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
|
||||
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
|
||||
acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_accel_acpi_driver_v4);
|
||||
platform_driver_unregister(&cmpc_accel_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_tablet_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_ipml_acpi_driver);
|
||||
platform_driver_unregister(&cmpc_keys_acpi_driver);
|
||||
}
|
||||
|
||||
module_init(cmpc_init);
|
||||
|
|
|
|||
|
|
@ -276,4 +276,9 @@ config DELL_WMI_SYSMAN
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called dell-wmi-sysman.
|
||||
|
||||
config DELL_DW5826E_RESET
|
||||
tristate "Dell DW5826e PLDR reset support"
|
||||
depends on ACPI
|
||||
help
|
||||
This adds support for the Dell DW5826e PLDR reset via ACPI
|
||||
endif # X86_PLATFORM_DRIVERS_DELL
|
||||
|
|
|
|||
|
|
@ -28,3 +28,4 @@ obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
|
|||
obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o
|
||||
obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
|
||||
obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/
|
||||
obj-$(CONFIG_DELL_DW5826E_RESET) += dell-dw5826e-reset.o
|
||||
|
|
|
|||
|
|
@ -12,8 +12,13 @@
|
|||
#include <linux/cleanup.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "alienware-wmi.h"
|
||||
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
|
||||
|
|
@ -150,22 +155,22 @@ u8 alienware_interface;
|
|||
int alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
|
||||
void *in_args, size_t in_size, u32 *out_data)
|
||||
{
|
||||
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct acpi_buffer in = {in_size, in_args};
|
||||
acpi_status ret;
|
||||
struct wmi_buffer out, in = {
|
||||
.data = in_args,
|
||||
.length = in_size,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL);
|
||||
if (ACPI_FAILURE(ret))
|
||||
return -EIO;
|
||||
if (!out_data)
|
||||
return wmidev_invoke_procedure(wdev, 0, method_id, &in);
|
||||
|
||||
union acpi_object *obj __free(kfree) = out.pointer;
|
||||
ret = wmidev_invoke_method(wdev, 0, method_id, &in, &out,
|
||||
sizeof(*out_data));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (out_data) {
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
*out_data = (u32)obj->integer.value;
|
||||
else
|
||||
return -ENOMSG;
|
||||
}
|
||||
__le32 *data __free(kfree) = out.data;
|
||||
*out_data = le32_to_cpu(*data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
93
drivers/platform/x86/dell/dell-dw5826e-reset.c
Normal file
93
drivers/platform/x86/dell/dell-dw5826e-reset.c
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* dell-dw5826e-reset.c - Dell DW5826e reset driver
|
||||
*
|
||||
* Copyright (C) 2026 Jackbb Wu <jackbb.wu@compal.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#define PALC_DSM_FN_TRIGGER_PLDR BIT(1)
|
||||
|
||||
static guid_t palc_dsm_guid =
|
||||
GUID_INIT(0x5a1a4bba, 0x8006, 0x487e, 0xbe, 0x0a, 0xac, 0xf5, 0xd8, 0xfd, 0xfe, 0x59);
|
||||
|
||||
static int trigger_palc_pldr(struct device *dev, acpi_handle handle)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR, NULL);
|
||||
if (!obj) {
|
||||
dev_err(dev, "Failed to evaluate _DSM\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_err(dev, "Unexpected _DSM return type: %d\n", obj->type);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
ACPI_FREE(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t wwan_reset_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
int ret;
|
||||
|
||||
ret = trigger_palc_pldr(dev, handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(wwan_reset);
|
||||
|
||||
static struct attribute *palc_attrs[] = {
|
||||
&dev_attr_wwan_reset.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(palc);
|
||||
|
||||
static int palc_probe(struct platform_device *pdev)
|
||||
{
|
||||
acpi_handle handle;
|
||||
|
||||
handle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
if (!acpi_check_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id palc_acpi_ids[] = {
|
||||
{ "PALC0001", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, palc_acpi_ids);
|
||||
|
||||
static struct platform_driver palc_driver = {
|
||||
.driver = {
|
||||
.name = "dell-dw5826e-reset",
|
||||
.acpi_match_table = palc_acpi_ids,
|
||||
.dev_groups = palc_groups,
|
||||
},
|
||||
.probe = palc_probe,
|
||||
};
|
||||
module_platform_driver(palc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Dell DW5826e reset driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("JackBB Wu");
|
||||
|
|
@ -222,6 +222,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
|
|||
},
|
||||
.driver_data = &quirk_dell_vostro_v130,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Dell Inspiron N5110",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N5110"),
|
||||
},
|
||||
.driver_data = &quirk_dell_vostro_v130,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Dell Vostro 3360",
|
||||
|
|
@ -2551,7 +2560,12 @@ static int __init dell_init(void)
|
|||
if (mute_led_registered)
|
||||
led_classdev_unregister(&mute_led_cdev);
|
||||
fail_led:
|
||||
dell_laptop_unregister_notifier(&dell_laptop_notifier);
|
||||
debugfs_remove_recursive(dell_laptop_dir);
|
||||
dell_battery_exit();
|
||||
kbd_led_exit();
|
||||
if (quirks && quirks->touchpad_led)
|
||||
touchpad_led_exit();
|
||||
dell_cleanup_rfkill();
|
||||
fail_rfkill:
|
||||
platform_device_del(platform_device);
|
||||
|
|
|
|||
|
|
@ -50,38 +50,32 @@ static inline struct wmi_smbios_priv *get_first_smbios_priv(void)
|
|||
|
||||
static int run_smbios_call(struct wmi_device *wdev)
|
||||
{
|
||||
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct wmi_smbios_priv *priv;
|
||||
struct acpi_buffer input;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
priv = dev_get_drvdata(&wdev->dev);
|
||||
input.length = priv->req_buf_size - sizeof(u64);
|
||||
input.pointer = &priv->buf->std;
|
||||
struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
const struct wmi_buffer input = {
|
||||
.length = priv->req_buf_size - sizeof(u64),
|
||||
.data = &priv->buf->std,
|
||||
};
|
||||
struct wmi_buffer output;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n",
|
||||
priv->buf->std.cmd_class, priv->buf->std.cmd_select,
|
||||
priv->buf->std.input[0], priv->buf->std.input[1],
|
||||
priv->buf->std.input[2], priv->buf->std.input[3]);
|
||||
|
||||
status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_dbg(&wdev->dev, "received type: %d\n", obj->type);
|
||||
if (obj->type == ACPI_TYPE_INTEGER)
|
||||
dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n",
|
||||
obj->integer.value);
|
||||
kfree(output.pointer);
|
||||
return -EIO;
|
||||
}
|
||||
memcpy(input.pointer, obj->buffer.pointer, obj->buffer.length);
|
||||
/*
|
||||
* The output buffer returned by the WMI method should have at least the size
|
||||
* of the input buffer.
|
||||
*/
|
||||
ret = wmidev_invoke_method(wdev, 0, 1, &input, &output, input.length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memcpy(input.data, output.data, input.length);
|
||||
kfree(output.data);
|
||||
dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n",
|
||||
priv->buf->std.output[0], priv->buf->std.output[1],
|
||||
priv->buf->std.output[2], priv->buf->std.output[3]);
|
||||
kfree(output.pointer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
|
@ -414,7 +415,8 @@ static void dell_wmi_switch_event(struct input_dev **subdev,
|
|||
input_sync(*subdev);
|
||||
}
|
||||
|
||||
static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 *buffer, int remaining)
|
||||
static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, __le16 *buffer,
|
||||
int remaining)
|
||||
{
|
||||
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
const struct key_entry *key;
|
||||
|
|
@ -446,15 +448,15 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16
|
|||
} else if (type == 0x0011 && code == 0xe070 && remaining > 0) {
|
||||
dell_wmi_switch_event(&priv->tabletswitch_dev,
|
||||
"Dell tablet mode switch",
|
||||
SW_TABLET_MODE, !buffer[0]);
|
||||
SW_TABLET_MODE, !le16_to_cpu(buffer[0]));
|
||||
return 1;
|
||||
} else if (type == 0x0012 && code == 0x000c && remaining > 0) {
|
||||
/* Eprivacy toggle, switch to "on" key entry for on events */
|
||||
if (buffer[0] == 2)
|
||||
if (le16_to_cpu(buffer[0]) == 2)
|
||||
key++;
|
||||
used = 1;
|
||||
} else if (type == 0x0012 && code == 0x000d && remaining > 0) {
|
||||
value = (buffer[2] == 2);
|
||||
value = (le16_to_cpu(buffer[2]) == 2);
|
||||
used = 1;
|
||||
}
|
||||
|
||||
|
|
@ -463,24 +465,17 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16
|
|||
return used;
|
||||
}
|
||||
|
||||
static void dell_wmi_notify(struct wmi_device *wdev,
|
||||
union acpi_object *obj)
|
||||
static void dell_wmi_notify(struct wmi_device *wdev, const struct wmi_buffer *buffer)
|
||||
{
|
||||
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
u16 *buffer_entry, *buffer_end;
|
||||
acpi_size buffer_size;
|
||||
__le16 *buffer_entry, *buffer_end;
|
||||
size_t buffer_size;
|
||||
int len, i;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
pr_warn("bad response type %x\n", obj->type);
|
||||
return;
|
||||
}
|
||||
pr_debug("Received WMI event (%*ph)\n", (int)buffer->length, buffer->data);
|
||||
|
||||
pr_debug("Received WMI event (%*ph)\n",
|
||||
obj->buffer.length, obj->buffer.pointer);
|
||||
|
||||
buffer_entry = (u16 *)obj->buffer.pointer;
|
||||
buffer_size = obj->buffer.length/2;
|
||||
buffer_entry = buffer->data;
|
||||
buffer_size = buffer->length / 2;
|
||||
buffer_end = buffer_entry + buffer_size;
|
||||
|
||||
/*
|
||||
|
|
@ -496,12 +491,12 @@ static void dell_wmi_notify(struct wmi_device *wdev,
|
|||
* one event on devices with WMI interface version 0.
|
||||
*/
|
||||
if (priv->interface_version == 0 && buffer_entry < buffer_end)
|
||||
if (buffer_end > buffer_entry + buffer_entry[0] + 1)
|
||||
buffer_end = buffer_entry + buffer_entry[0] + 1;
|
||||
if (buffer_end > buffer_entry + le16_to_cpu(buffer_entry[0]) + 1)
|
||||
buffer_end = buffer_entry + le16_to_cpu(buffer_entry[0]) + 1;
|
||||
|
||||
while (buffer_entry < buffer_end) {
|
||||
|
||||
len = buffer_entry[0];
|
||||
len = le16_to_cpu(buffer_entry[0]);
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
|
|
@ -514,11 +509,11 @@ static void dell_wmi_notify(struct wmi_device *wdev,
|
|||
|
||||
pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
|
||||
|
||||
switch (buffer_entry[1]) {
|
||||
switch (le16_to_cpu(buffer_entry[1])) {
|
||||
case 0x0000: /* One key pressed or event occurred */
|
||||
if (len > 2)
|
||||
dell_wmi_process_key(wdev, buffer_entry[1],
|
||||
buffer_entry[2],
|
||||
dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]),
|
||||
le16_to_cpu(buffer_entry[2]),
|
||||
buffer_entry + 3,
|
||||
len - 3);
|
||||
/* Extended data is currently ignored */
|
||||
|
|
@ -526,22 +521,23 @@ static void dell_wmi_notify(struct wmi_device *wdev,
|
|||
case 0x0010: /* Sequence of keys pressed */
|
||||
case 0x0011: /* Sequence of events occurred */
|
||||
for (i = 2; i < len; ++i)
|
||||
i += dell_wmi_process_key(wdev, buffer_entry[1],
|
||||
buffer_entry[i],
|
||||
i += dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]),
|
||||
le16_to_cpu(buffer_entry[i]),
|
||||
buffer_entry + i,
|
||||
len - i - 1);
|
||||
break;
|
||||
case 0x0012:
|
||||
if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3],
|
||||
buffer_entry[4]))
|
||||
if ((len > 4) && dell_privacy_process_event(le16_to_cpu(buffer_entry[1]),
|
||||
le16_to_cpu(buffer_entry[3]),
|
||||
le16_to_cpu(buffer_entry[4])))
|
||||
/* dell_privacy_process_event has handled the event */;
|
||||
else if (len > 2)
|
||||
dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2],
|
||||
dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]),
|
||||
le16_to_cpu(buffer_entry[2]),
|
||||
buffer_entry + 3, len - 3);
|
||||
break;
|
||||
default: /* Unknown event */
|
||||
pr_info("Unknown WMI event type 0x%x\n",
|
||||
(int)buffer_entry[1]);
|
||||
pr_info("Unknown WMI event type 0x%x\n", le16_to_cpu(buffer_entry[1]));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -825,10 +821,10 @@ static struct wmi_driver dell_wmi_driver = {
|
|||
.name = "dell-wmi",
|
||||
},
|
||||
.id_table = dell_wmi_id_table,
|
||||
.min_event_size = sizeof(u16),
|
||||
.min_event_size = sizeof(__le16),
|
||||
.probe = dell_wmi_probe,
|
||||
.remove = dell_wmi_remove,
|
||||
.notify = dell_wmi_notify,
|
||||
.notify_new = dell_wmi_notify,
|
||||
};
|
||||
|
||||
static int __init dell_wmi_init(void)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
#define pr_format(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/device/driver.h>
|
||||
|
|
@ -99,6 +100,11 @@ enum dell_ddv_method {
|
|||
DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22,
|
||||
};
|
||||
|
||||
struct dell_wmi_buffer {
|
||||
__le32 raw_size;
|
||||
u8 raw_data[];
|
||||
} __packed;
|
||||
|
||||
struct fan_sensor_entry {
|
||||
u8 type;
|
||||
__le16 rpm;
|
||||
|
|
@ -126,7 +132,7 @@ struct dell_wmi_ddv_sensors {
|
|||
bool active;
|
||||
struct mutex lock; /* protect caching */
|
||||
unsigned long timestamp;
|
||||
union acpi_object *obj;
|
||||
struct dell_wmi_buffer *buffer;
|
||||
u64 entries;
|
||||
};
|
||||
|
||||
|
|
@ -158,105 +164,96 @@ static const char * const fan_dock_labels[] = {
|
|||
"Docking Chipset Fan",
|
||||
};
|
||||
|
||||
static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg,
|
||||
union acpi_object **result, acpi_object_type type)
|
||||
static int dell_wmi_ddv_query(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg,
|
||||
struct wmi_buffer *output, size_t min_size)
|
||||
{
|
||||
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
const struct acpi_buffer in = {
|
||||
.length = sizeof(arg),
|
||||
.pointer = &arg,
|
||||
__le32 arg2 = cpu_to_le32(arg);
|
||||
const struct wmi_buffer input = {
|
||||
.length = sizeof(arg2),
|
||||
.data = &arg2,
|
||||
};
|
||||
union acpi_object *obj;
|
||||
acpi_status ret;
|
||||
|
||||
ret = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
|
||||
if (ACPI_FAILURE(ret))
|
||||
return -EIO;
|
||||
|
||||
obj = out.pointer;
|
||||
if (!obj)
|
||||
return -ENODATA;
|
||||
|
||||
if (obj->type != type) {
|
||||
kfree(obj);
|
||||
return -ENOMSG;
|
||||
}
|
||||
|
||||
*result = obj;
|
||||
|
||||
return 0;
|
||||
return wmidev_invoke_method(wdev, 0x0, method, &input, output, min_size);
|
||||
}
|
||||
|
||||
static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method,
|
||||
u32 arg, u32 *res)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
struct wmi_buffer output;
|
||||
int ret;
|
||||
|
||||
ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_INTEGER);
|
||||
ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(__le32));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (obj->integer.value <= U32_MAX)
|
||||
*res = (u32)obj->integer.value;
|
||||
else
|
||||
ret = -ERANGE;
|
||||
__le32 *argr __free(kfree) = output.data;
|
||||
|
||||
kfree(obj);
|
||||
*res = le32_to_cpu(*argr);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method,
|
||||
u32 arg, union acpi_object **result)
|
||||
u32 arg, struct dell_wmi_buffer **result)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
u64 buffer_size;
|
||||
struct dell_wmi_buffer *buffer;
|
||||
struct wmi_buffer output;
|
||||
size_t buffer_size;
|
||||
int ret;
|
||||
|
||||
ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_PACKAGE);
|
||||
ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(*buffer));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (obj->package.count != 2 ||
|
||||
obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
|
||||
obj->package.elements[1].type != ACPI_TYPE_BUFFER) {
|
||||
ret = -ENOMSG;
|
||||
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
buffer_size = obj->package.elements[0].integer.value;
|
||||
|
||||
if (!buffer_size) {
|
||||
buffer = output.data;
|
||||
if (!le32_to_cpu(buffer->raw_size)) {
|
||||
ret = -ENODATA;
|
||||
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (buffer_size > obj->package.elements[1].buffer.length) {
|
||||
buffer_size = struct_size(buffer, raw_data, le32_to_cpu(buffer->raw_size));
|
||||
if (buffer_size > output.length) {
|
||||
dev_warn(&wdev->dev,
|
||||
FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n",
|
||||
buffer_size, obj->package.elements[1].buffer.length);
|
||||
FW_WARN "Dell WMI buffer size (%zu) exceeds WMI buffer size (%zu)\n",
|
||||
buffer_size, output.length);
|
||||
ret = -EMSGSIZE;
|
||||
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
*result = obj;
|
||||
*result = buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(obj);
|
||||
kfree(output.data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method,
|
||||
u32 arg, union acpi_object **result)
|
||||
static ssize_t dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method,
|
||||
u32 arg, char *buf, size_t length)
|
||||
{
|
||||
return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING);
|
||||
struct wmi_buffer output;
|
||||
size_t str_size;
|
||||
int ret;
|
||||
|
||||
ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(struct wmi_string));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
struct wmi_string *str __free(kfree) = output.data;
|
||||
|
||||
str_size = sizeof(*str) + le16_to_cpu(str->length);
|
||||
if (str_size > output.length) {
|
||||
dev_warn(&wdev->dev,
|
||||
FW_WARN "WMI string size (%zu) exceeds WMI buffer size (%zu)\n",
|
||||
str_size, output.length);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return wmi_string_to_utf8s(str, buf, length);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -265,28 +262,26 @@ static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_meth
|
|||
static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_method method,
|
||||
struct dell_wmi_ddv_sensors *sensors, size_t entry_size)
|
||||
{
|
||||
struct dell_wmi_buffer *buffer;
|
||||
u64 buffer_size, rem, entries;
|
||||
union acpi_object *obj;
|
||||
u8 *buffer;
|
||||
int ret;
|
||||
|
||||
if (sensors->obj) {
|
||||
if (sensors->buffer) {
|
||||
if (time_before(jiffies, sensors->timestamp + HZ))
|
||||
return 0;
|
||||
|
||||
kfree(sensors->obj);
|
||||
sensors->obj = NULL;
|
||||
kfree(sensors->buffer);
|
||||
sensors->buffer = NULL;
|
||||
}
|
||||
|
||||
ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &obj);
|
||||
ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* buffer format sanity check */
|
||||
buffer_size = obj->package.elements[0].integer.value;
|
||||
buffer = obj->package.elements[1].buffer.pointer;
|
||||
buffer_size = le32_to_cpu(buffer->raw_size);
|
||||
entries = div64_u64_rem(buffer_size, entry_size, &rem);
|
||||
if (rem != 1 || buffer[buffer_size - 1] != 0xff) {
|
||||
if (rem != 1 || buffer->raw_data[buffer_size - 1] != 0xff) {
|
||||
ret = -ENOMSG;
|
||||
goto err_free;
|
||||
}
|
||||
|
|
@ -296,14 +291,14 @@ static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_me
|
|||
goto err_free;
|
||||
}
|
||||
|
||||
sensors->obj = obj;
|
||||
sensors->buffer = buffer;
|
||||
sensors->entries = entries;
|
||||
sensors->timestamp = jiffies;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(obj);
|
||||
kfree(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -328,7 +323,7 @@ static int dell_wmi_ddv_fan_read_channel(struct dell_wmi_ddv_data *data, u32 att
|
|||
if (channel >= data->fans.entries)
|
||||
return -ENXIO;
|
||||
|
||||
entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer;
|
||||
entry = (struct fan_sensor_entry *)data->fans.buffer->raw_data;
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
*val = get_unaligned_le16(&entry[channel].rpm);
|
||||
|
|
@ -354,7 +349,7 @@ static int dell_wmi_ddv_temp_read_channel(struct dell_wmi_ddv_data *data, u32 at
|
|||
if (channel >= data->temps.entries)
|
||||
return -ENXIO;
|
||||
|
||||
entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer;
|
||||
entry = (struct thermal_sensor_entry *)data->temps.buffer->raw_data;
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
*val = entry[channel].now * 1000;
|
||||
|
|
@ -411,7 +406,7 @@ static int dell_wmi_ddv_fan_read_string(struct dell_wmi_ddv_data *data, int chan
|
|||
if (channel >= data->fans.entries)
|
||||
return -ENXIO;
|
||||
|
||||
entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer;
|
||||
entry = (struct fan_sensor_entry *)data->fans.buffer->raw_data;
|
||||
type = entry[channel].type;
|
||||
switch (type) {
|
||||
case 0x00 ... 0x07:
|
||||
|
|
@ -442,7 +437,7 @@ static int dell_wmi_ddv_temp_read_string(struct dell_wmi_ddv_data *data, int cha
|
|||
if (channel >= data->temps.entries)
|
||||
return -ENXIO;
|
||||
|
||||
entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer;
|
||||
entry = (struct thermal_sensor_entry *)data->temps.buffer->raw_data;
|
||||
switch (entry[channel].type) {
|
||||
case 0x00:
|
||||
*str = "CPU";
|
||||
|
|
@ -553,8 +548,8 @@ static void dell_wmi_ddv_hwmon_cache_invalidate(struct dell_wmi_ddv_sensors *sen
|
|||
return;
|
||||
|
||||
mutex_lock(&sensors->lock);
|
||||
kfree(sensors->obj);
|
||||
sensors->obj = NULL;
|
||||
kfree(sensors->buffer);
|
||||
sensors->buffer = NULL;
|
||||
mutex_unlock(&sensors->lock);
|
||||
}
|
||||
|
||||
|
|
@ -564,7 +559,7 @@ static void dell_wmi_ddv_hwmon_cache_destroy(void *data)
|
|||
|
||||
sensors->active = false;
|
||||
mutex_destroy(&sensors->lock);
|
||||
kfree(sensors->obj);
|
||||
kfree(sensors->buffer);
|
||||
}
|
||||
|
||||
static struct hwmon_channel_info *dell_wmi_ddv_channel_init(struct wmi_device *wdev,
|
||||
|
|
@ -750,7 +745,7 @@ static void dell_wmi_battery_invalidate(struct dell_wmi_ddv_data *data,
|
|||
static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr);
|
||||
union acpi_object *obj;
|
||||
ssize_t count;
|
||||
u32 index;
|
||||
int ret;
|
||||
|
||||
|
|
@ -758,19 +753,19 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, &obj);
|
||||
count = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, buf,
|
||||
PAGE_SIZE);
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
if (count != DELL_EPPID_LENGTH && count != DELL_EPPID_EXT_LENGTH)
|
||||
dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%zd)\n", count);
|
||||
|
||||
ret = sysfs_emit_at(buf, count, "\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (obj->string.length != DELL_EPPID_LENGTH && obj->string.length != DELL_EPPID_EXT_LENGTH)
|
||||
dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%d)\n",
|
||||
obj->string.length);
|
||||
|
||||
ret = sysfs_emit(buf, "%s\n", obj->string.pointer);
|
||||
|
||||
kfree(obj);
|
||||
|
||||
return ret;
|
||||
return count + ret;
|
||||
}
|
||||
|
||||
static int dell_wmi_ddv_get_health(struct dell_wmi_ddv_data *data, u32 index,
|
||||
|
|
@ -994,19 +989,15 @@ static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method m
|
|||
{
|
||||
struct device *dev = seq->private;
|
||||
struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
|
||||
union acpi_object *obj;
|
||||
u64 size;
|
||||
u8 *buf;
|
||||
struct dell_wmi_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj);
|
||||
ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
size = obj->package.elements[0].integer.value;
|
||||
buf = obj->package.elements[1].buffer.pointer;
|
||||
ret = seq_write(seq, buf, size);
|
||||
kfree(obj);
|
||||
ret = seq_write(seq, buffer->raw_data, le32_to_cpu(buffer->raw_size));
|
||||
kfree(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,35 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
#include "dell-wmi-descriptor.h"
|
||||
|
||||
#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
|
||||
|
||||
/*
|
||||
* Descriptor buffer is 128 byte long and contains:
|
||||
*
|
||||
* Name Offset Length Value
|
||||
* Vendor Signature 0 4 "DELL"
|
||||
* Object Signature 4 4 " WMI"
|
||||
* WMI Interface Version 8 4 <version>
|
||||
* WMI buffer length 12 4 <length>
|
||||
* WMI hotfix number 16 4 <hotfix>
|
||||
*/
|
||||
struct descriptor {
|
||||
/* Both fields are NOT null-terminated */
|
||||
char vendor_signature[4] __nonstring;
|
||||
char object_signature[4] __nonstring;
|
||||
__le32 interface_version;
|
||||
__le32 buffer_length;
|
||||
__le32 hotfix_number;
|
||||
} __packed;
|
||||
|
||||
struct descriptor_priv {
|
||||
struct list_head list;
|
||||
u32 interface_version;
|
||||
|
|
@ -88,77 +109,46 @@ bool dell_wmi_get_hotfix(u32 *hotfix)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix);
|
||||
|
||||
/*
|
||||
* Descriptor buffer is 128 byte long and contains:
|
||||
*
|
||||
* Name Offset Length Value
|
||||
* Vendor Signature 0 4 "DELL"
|
||||
* Object Signature 4 4 " WMI"
|
||||
* WMI Interface Version 8 4 <version>
|
||||
* WMI buffer length 12 4 <length>
|
||||
* WMI hotfix number 16 4 <hotfix>
|
||||
*/
|
||||
static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
|
||||
const void *context)
|
||||
static int dell_wmi_descriptor_probe(struct wmi_device *wdev, const void *context)
|
||||
{
|
||||
union acpi_object *obj = NULL;
|
||||
struct descriptor_priv *priv;
|
||||
u32 *buffer;
|
||||
struct wmi_buffer buffer;
|
||||
int ret;
|
||||
|
||||
obj = wmidev_block_query(wdev, 0);
|
||||
if (!obj) {
|
||||
dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
ret = wmidev_query_block(wdev, 0, &buffer, sizeof(struct descriptor));
|
||||
if (ret < 0) {
|
||||
descriptor_valid = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
|
||||
ret = -EINVAL;
|
||||
descriptor_valid = ret;
|
||||
goto out;
|
||||
struct descriptor *desc __free(kfree) = buffer.data;
|
||||
|
||||
if (strncmp(desc->vendor_signature, "DELL", sizeof(desc->vendor_signature))) {
|
||||
dev_err(&wdev->dev, "Dell descriptor buffer has invalid vendor signature (%4ph)\n",
|
||||
desc->vendor_signature);
|
||||
descriptor_valid = -ENOMSG;
|
||||
return -ENOMSG;
|
||||
}
|
||||
|
||||
/* Although it's not technically a failure, this would lead to
|
||||
* unexpected behavior
|
||||
*/
|
||||
if (obj->buffer.length != 128) {
|
||||
dev_err(&wdev->dev,
|
||||
"Dell descriptor buffer has unexpected length (%d)\n",
|
||||
obj->buffer.length);
|
||||
ret = -EINVAL;
|
||||
descriptor_valid = ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = (u32 *)obj->buffer.pointer;
|
||||
|
||||
if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
|
||||
dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
|
||||
buffer);
|
||||
ret = -EINVAL;
|
||||
descriptor_valid = ret;
|
||||
goto out;
|
||||
if (strncmp(desc->object_signature, " WMI", sizeof(desc->object_signature))) {
|
||||
dev_err(&wdev->dev, "Dell descriptor buffer has invalid object signature (%4ph)\n",
|
||||
desc->object_signature);
|
||||
descriptor_valid = -ENOMSG;
|
||||
return -ENOMSG;
|
||||
}
|
||||
descriptor_valid = 0;
|
||||
|
||||
if (buffer[2] != 0 && buffer[2] != 1)
|
||||
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
|
||||
(unsigned long) buffer[2]);
|
||||
if (le32_to_cpu(desc->interface_version) > 1)
|
||||
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n",
|
||||
le32_to_cpu(desc->interface_version));
|
||||
|
||||
priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
|
||||
GFP_KERNEL);
|
||||
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->interface_version = buffer[2];
|
||||
priv->size = buffer[3];
|
||||
priv->hotfix = buffer[4];
|
||||
ret = 0;
|
||||
priv->interface_version = le32_to_cpu(desc->interface_version);
|
||||
priv->size = le32_to_cpu(desc->buffer_length);
|
||||
priv->hotfix = le32_to_cpu(desc->hotfix_number);
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
mutex_lock(&list_mutex);
|
||||
list_add_tail(&priv->list, &wmi_list);
|
||||
|
|
@ -169,8 +159,6 @@ static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
|
|||
(unsigned long) priv->size,
|
||||
(unsigned long) priv->hotfix);
|
||||
|
||||
out:
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,14 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include "dell-wmi-privacy.h"
|
||||
|
|
@ -25,6 +28,26 @@
|
|||
#define DELL_PRIVACY_CAMERA_EVENT 0x2
|
||||
#define led_to_priv(c) container_of(c, struct privacy_wmi_data, cdev)
|
||||
|
||||
/*
|
||||
* Describes the Device State class exposed by BIOS which can be consumed by
|
||||
* various applications interested in knowing the Privacy feature capabilities.
|
||||
* class DeviceState
|
||||
* {
|
||||
* [key, read] string InstanceName;
|
||||
* [read] boolean ReadOnly;
|
||||
*
|
||||
* [WmiDataId(1), read] uint32 DevicesSupported;
|
||||
* 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen
|
||||
*
|
||||
* [WmiDataId(2), read] uint32 CurrentState;
|
||||
* 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen
|
||||
* };
|
||||
*/
|
||||
struct device_state {
|
||||
__le32 devices_supported;
|
||||
__le32 current_state;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The wmi_list is used to store the privacy_priv struct with mutex protecting
|
||||
*/
|
||||
|
|
@ -185,60 +208,28 @@ static struct attribute *privacy_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(privacy);
|
||||
|
||||
/*
|
||||
* Describes the Device State class exposed by BIOS which can be consumed by
|
||||
* various applications interested in knowing the Privacy feature capabilities.
|
||||
* class DeviceState
|
||||
* {
|
||||
* [key, read] string InstanceName;
|
||||
* [read] boolean ReadOnly;
|
||||
*
|
||||
* [WmiDataId(1), read] uint32 DevicesSupported;
|
||||
* 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen
|
||||
*
|
||||
* [WmiDataId(2), read] uint32 CurrentState;
|
||||
* 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen
|
||||
* };
|
||||
*/
|
||||
static int get_current_status(struct wmi_device *wdev)
|
||||
{
|
||||
struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev);
|
||||
union acpi_object *obj_present;
|
||||
u32 *buffer;
|
||||
int ret = 0;
|
||||
struct wmi_buffer buffer;
|
||||
int ret;
|
||||
|
||||
if (!priv) {
|
||||
dev_err(&wdev->dev, "dell privacy priv is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check privacy support features and device states */
|
||||
obj_present = wmidev_block_query(wdev, 0);
|
||||
if (!obj_present) {
|
||||
dev_err(&wdev->dev, "failed to read Binary MOF\n");
|
||||
return -EIO;
|
||||
}
|
||||
ret = wmidev_query_block(wdev, 0, &buffer, sizeof(struct device_state));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (obj_present->type != ACPI_TYPE_BUFFER) {
|
||||
dev_err(&wdev->dev, "Binary MOF is not a buffer!\n");
|
||||
ret = -EIO;
|
||||
goto obj_free;
|
||||
}
|
||||
/* Although it's not technically a failure, this would lead to
|
||||
* unexpected behavior
|
||||
*/
|
||||
if (obj_present->buffer.length != 8) {
|
||||
dev_err(&wdev->dev, "Dell privacy buffer has unexpected length (%d)!\n",
|
||||
obj_present->buffer.length);
|
||||
ret = -EINVAL;
|
||||
goto obj_free;
|
||||
}
|
||||
buffer = (u32 *)obj_present->buffer.pointer;
|
||||
priv->features_present = buffer[0];
|
||||
priv->last_status = buffer[1];
|
||||
struct device_state *state __free(kfree) = buffer.data;
|
||||
|
||||
obj_free:
|
||||
kfree(obj_present);
|
||||
return ret;
|
||||
priv->features_present = le32_to_cpu(state->devices_supported);
|
||||
priv->last_status = le32_to_cpu(state->current_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ bool dell_privacy_has_mic_mute(void);
|
|||
bool dell_privacy_process_event(int type, int code, int status);
|
||||
int dell_privacy_register_driver(void);
|
||||
void dell_privacy_unregister_driver(void);
|
||||
#else /* CONFIG_DELL_PRIVACY */
|
||||
#else /* CONFIG_DELL_WMI_PRIVACY */
|
||||
static inline bool dell_privacy_has_mic_mute(void)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -32,5 +32,5 @@ static inline int dell_privacy_register_driver(void)
|
|||
static inline void dell_privacy_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DELL_PRIVACY */
|
||||
#endif /* CONFIG_DELL_WMI_PRIVACY */
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
#define SETBIOSDEFAULTS_METHOD_ID 0x03
|
||||
#define SETATTRIBUTE_METHOD_ID 0x04
|
||||
|
||||
static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size,
|
||||
int method_id)
|
||||
static int call_biosattributes_interface(struct wmi_device *wdev, u8 *in_args,
|
||||
size_t size, int method_id)
|
||||
{
|
||||
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct acpi_buffer input;
|
||||
|
|
@ -51,7 +51,7 @@ int set_attribute(const char *a_name, const char *a_value)
|
|||
{
|
||||
size_t security_area_size, buffer_size;
|
||||
size_t a_name_size, a_value_size;
|
||||
char *buffer = NULL, *start;
|
||||
u8 *buffer = NULL, *start;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
|
|
@ -109,7 +109,7 @@ int set_bios_defaults(u8 deftype)
|
|||
{
|
||||
size_t security_area_size, buffer_size;
|
||||
size_t integer_area_size = sizeof(u8);
|
||||
char *buffer = NULL;
|
||||
u8 *buffer = NULL;
|
||||
u8 *defaultType;
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ int init_bios_attr_set_interface(void);
|
|||
int map_wmi_error(int error_code);
|
||||
size_t calculate_string_buffer(const char *str);
|
||||
size_t calculate_security_buffer(const char *authentication);
|
||||
void populate_security_buffer(char *buffer, const char *authentication);
|
||||
ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str);
|
||||
void populate_security_buffer(u8 *buffer, const char *authentication);
|
||||
ssize_t populate_string_buffer(u8 *buffer, size_t buffer_len, const char *str);
|
||||
int set_new_password(const char *password_type, const char *new);
|
||||
int init_bios_attr_pass_interface(void);
|
||||
void exit_bios_attr_pass_interface(void);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include <linux/wmi.h>
|
||||
#include "dell-wmi-sysman.h"
|
||||
|
||||
static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size)
|
||||
static int call_password_interface(struct wmi_device *wdev, u8 *in_args, size_t size)
|
||||
{
|
||||
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct acpi_buffer input;
|
||||
|
|
@ -42,7 +42,7 @@ int set_new_password(const char *password_type, const char *new)
|
|||
{
|
||||
size_t password_type_size, current_password_size, new_size;
|
||||
size_t security_area_size, buffer_size;
|
||||
char *buffer = NULL, *start;
|
||||
u8 *buffer = NULL, *start;
|
||||
char *current_password;
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ static int reset_option = -1;
|
|||
* @buffer_len: length of the destination buffer
|
||||
* @str: the string to insert into buffer
|
||||
*/
|
||||
ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str)
|
||||
ssize_t populate_string_buffer(u8 *buffer, size_t buffer_len, const char *str)
|
||||
{
|
||||
u16 *length = (u16 *)buffer;
|
||||
u16 *target = length + 1;
|
||||
|
|
@ -87,10 +87,10 @@ size_t calculate_security_buffer(const char *authentication)
|
|||
*
|
||||
* Currently only supported type is PLAIN TEXT
|
||||
*/
|
||||
void populate_security_buffer(char *buffer, const char *authentication)
|
||||
void populate_security_buffer(u8 *buffer, const char *authentication)
|
||||
{
|
||||
size_t seclen = strlen(authentication);
|
||||
char *auth = buffer + sizeof(u32) * 2;
|
||||
u8 *auth = buffer + sizeof(u32) * 2;
|
||||
u32 *sectype = (u32 *) buffer;
|
||||
u32 *seclenp = sectype + 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -562,9 +562,9 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj,
|
|||
buffer[count] = '\0';
|
||||
|
||||
if (strstr(buffer, "mono"))
|
||||
strcpy(image_type, "mono");
|
||||
strscpy(image_type, "mono");
|
||||
else if (strstr(buffer, "packet"))
|
||||
strcpy(image_type, "packet");
|
||||
strscpy(image_type, "packet");
|
||||
else if (strstr(buffer, "init")) {
|
||||
/*
|
||||
* If due to the user error the driver gets in a bad
|
||||
|
|
|
|||
|
|
@ -205,6 +205,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
|
|||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAB") },
|
||||
.driver_data = (void *)&omen_v1_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8B2F") },
|
||||
.driver_data = (void *)&victus_s_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") },
|
||||
.driver_data = (void *)&victus_s_thermal_params,
|
||||
|
|
@ -249,6 +253,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
|
|||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") },
|
||||
.driver_data = (void *)&victus_s_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D26") },
|
||||
.driver_data = (void *)&omen_v1_legacy_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") },
|
||||
.driver_data = (void *)&omen_v1_no_ec_thermal_params,
|
||||
|
|
@ -257,6 +265,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
|
|||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") },
|
||||
.driver_data = (void *)&omen_v1_no_ec_thermal_params,
|
||||
},
|
||||
{
|
||||
.matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") },
|
||||
.driver_data = (void *)&omen_v1_legacy_thermal_params,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
@ -2396,7 +2408,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
|
|||
ret = hp_wmi_fan_speed_max_set(1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mod_delayed_work(system_wq, &priv->keep_alive_dwork,
|
||||
mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork,
|
||||
secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
|
||||
return 0;
|
||||
case PWM_MODE_MANUAL:
|
||||
|
|
@ -2405,7 +2417,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
|
|||
ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mod_delayed_work(system_wq, &priv->keep_alive_dwork,
|
||||
mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork,
|
||||
secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
|
||||
return 0;
|
||||
case PWM_MODE_AUTO:
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/suspend.h>
|
||||
|
|
@ -156,6 +158,13 @@ static const struct dmi_system_id button_array_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "HP ProBook x360 440 G1",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 440 G1"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -230,6 +239,7 @@ static const struct dmi_system_id dmi_auto_add_switch[] = {
|
|||
};
|
||||
|
||||
struct intel_hid_priv {
|
||||
struct mutex mutex; /* Avoid notify_handler() racing with itself */
|
||||
struct input_dev *input_dev;
|
||||
struct input_dev *array;
|
||||
struct input_dev *switches;
|
||||
|
|
@ -565,6 +575,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
|
|||
struct key_entry *ke;
|
||||
int err;
|
||||
|
||||
guard(mutex)(&priv->mutex);
|
||||
|
||||
/*
|
||||
* Some convertible have unreliable VGBS return which could cause incorrect
|
||||
* SW_TABLET_MODE report, in these cases we enable support when receiving
|
||||
|
|
@ -720,6 +732,10 @@ static int intel_hid_probe(struct platform_device *device)
|
|||
return -ENOMEM;
|
||||
dev_set_drvdata(&device->dev, priv);
|
||||
|
||||
err = devm_mutex_init(&device->dev, &priv->mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* See dual_accel_detect.h for more info on the dual_accel check. */
|
||||
if (enable_sw_tablet_mode == TABLET_SW_AUTO) {
|
||||
if (dmi_check_system(dmi_vgbs_allow_list))
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ config INTEL_PMC_CORE
|
|||
depends on ACPI
|
||||
depends on INTEL_PMT_TELEMETRY
|
||||
select INTEL_PMC_SSRAM_TELEMETRY
|
||||
select INTEL_PMC_PWRM_TELEMETRY
|
||||
help
|
||||
The Intel Platform Controller Hub for Intel Core SoCs provides access
|
||||
to Power Management Controller registers via various interfaces. This
|
||||
|
|
@ -28,3 +29,27 @@ config INTEL_PMC_CORE
|
|||
|
||||
config INTEL_PMC_SSRAM_TELEMETRY
|
||||
tristate
|
||||
help
|
||||
This driver discovers PMC SSRAM telemetry regions through the PMC's
|
||||
MMIO interface (PCI) or ACPI _DSD properties and registers them with
|
||||
the Intel VSEC framework as Intel PMT telemetry devices.
|
||||
|
||||
It probes the PMC SSRAM device, extracts DVSEC information from MMIO,
|
||||
reads device IDs and base addresses for multiple PMCs (main, IOE, PCH),
|
||||
and exposes the discovered telemetry through Intel PMT interfaces
|
||||
(including sysfs).
|
||||
|
||||
This option is selected by INTEL_PMC_CORE.
|
||||
|
||||
config INTEL_PMC_PWRM_TELEMETRY
|
||||
tristate
|
||||
help
|
||||
This driver discovers PMC PWRM telemetry regions described in ACPI
|
||||
_DSD and registers them with the Intel VSEC framework as Intel PMT
|
||||
telemetry devices.
|
||||
|
||||
It validates the ACPI discovery data and publishes the discovered
|
||||
regions so they can be accessed through the Intel PMT telemetry
|
||||
interfaces (including sysfs).
|
||||
|
||||
This option is selected by INTEL_PMC_CORE.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
#
|
||||
|
||||
intel_pmc_core-y := core.o spt.o cnp.o icl.o \
|
||||
tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o
|
||||
tgl.o adl.o mtl.o arl.o \
|
||||
lnl.o ptl.o wcl.o nvl.o
|
||||
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
|
||||
intel_pmc_core_pltdrv-y := pltdrv.o
|
||||
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o
|
||||
|
|
@ -12,3 +13,5 @@ obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o
|
|||
# Intel PMC SSRAM driver
|
||||
intel_pmc_ssram_telemetry-y += ssram_telemetry.o
|
||||
obj-$(CONFIG_INTEL_PMC_SSRAM_TELEMETRY) += intel_pmc_ssram_telemetry.o
|
||||
intel_pmc_pwrm_telemetry-y += pwrm_telemetry.o
|
||||
obj-$(CONFIG_INTEL_PMC_PWRM_TELEMETRY) += intel_pmc_pwrm_telemetry.o
|
||||
|
|
|
|||
|
|
@ -672,6 +672,9 @@ static struct pmc_info arl_pmc_info_list[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const u8 arl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE, PMC_IDX_PCH};
|
||||
static const u8 arl_h_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE};
|
||||
|
||||
#define ARL_NPU_PCI_DEV 0xad1d
|
||||
#define ARL_GNA_PCI_DEV 0xae4c
|
||||
#define ARL_H_NPU_PCI_DEV 0x7d1d
|
||||
|
|
@ -720,8 +723,9 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_
|
|||
|
||||
static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0};
|
||||
struct pmc_dev_info arl_pmc_dev = {
|
||||
.pci_func = 0,
|
||||
.dmu_guids = ARL_PMT_DMU_GUIDS,
|
||||
.num_pmcs = ARRAY_SIZE(arl_pmc_list),
|
||||
.pmc_list = arl_pmc_list,
|
||||
.regmap_list = arl_pmc_info_list,
|
||||
.map = &arl_socs_reg_map,
|
||||
.sub_req_show = &pmc_core_substate_req_regs_fops,
|
||||
|
|
@ -729,12 +733,15 @@ struct pmc_dev_info arl_pmc_dev = {
|
|||
.resume = arl_resume,
|
||||
.init = arl_core_init,
|
||||
.sub_req = pmc_core_pmt_get_lpm_req,
|
||||
.ssram_hidden = true,
|
||||
.die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET,
|
||||
};
|
||||
|
||||
static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0};
|
||||
struct pmc_dev_info arl_h_pmc_dev = {
|
||||
.pci_func = 2,
|
||||
.dmu_guids = ARL_H_PMT_DMU_GUIDS,
|
||||
.num_pmcs = ARRAY_SIZE(arl_h_pmc_list),
|
||||
.pmc_list = arl_h_pmc_list,
|
||||
.regmap_list = arl_pmc_info_list,
|
||||
.map = &mtl_socm_reg_map,
|
||||
.sub_req_show = &pmc_core_substate_req_regs_fops,
|
||||
|
|
@ -742,4 +749,6 @@ struct pmc_dev_info arl_h_pmc_dev = {
|
|||
.resume = arl_h_resume,
|
||||
.init = arl_h_core_init,
|
||||
.sub_req = pmc_core_pmt_get_lpm_req,
|
||||
.ssram_hidden = true,
|
||||
.die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -623,7 +623,8 @@ static u32 convert_ltr_scale(u32 val)
|
|||
* ----------------------------------------------
|
||||
*/
|
||||
if (val > 5) {
|
||||
pr_warn("Invalid LTR scale factor.\n");
|
||||
pr_warn_once("Invalid LTR scale factor %u (only 0-5 are valid per PCIe spec)\n",
|
||||
val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1071,6 +1072,44 @@ static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused)
|
|||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us);
|
||||
|
||||
static int pmc_core_pkgc_counters_show(struct seq_file *s,
|
||||
struct telem_endpoint *ep,
|
||||
u32 offset, const char **counters)
|
||||
{
|
||||
unsigned int i;
|
||||
u32 counter;
|
||||
int ret;
|
||||
|
||||
for (i = 0; counters[i]; i++) {
|
||||
ret = pmt_telem_read32(ep, offset + i, &counter, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
seq_printf(s, "%-30s %-30u\n", counters[i], counter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
|
||||
return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep,
|
||||
pmcdev->pkgc_ltr_blocker_offset,
|
||||
pmcdev->pkgc_ltr_blocker_counters);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker);
|
||||
|
||||
static int pmc_core_pkgc_blocker_residency_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
|
||||
return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep,
|
||||
pmcdev->pkgc_blocker_offset,
|
||||
pmcdev->pkgc_blocker_counters);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_blocker_residency);
|
||||
|
||||
static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
|
|
@ -1322,28 +1361,45 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev,
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids)
|
||||
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
|
||||
{
|
||||
struct telem_endpoint *ep;
|
||||
struct pci_dev *pcidev;
|
||||
|
||||
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0));
|
||||
struct pci_dev *pcidev __free(pci_dev_put) = pci_get_domain_bus_and_slot(0, 0,
|
||||
PCI_DEVFN(10, 0));
|
||||
if (!pcidev) {
|
||||
dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
ep = pmc_core_register_endpoint(pcidev, guids);
|
||||
pci_dev_put(pcidev);
|
||||
if (IS_ERR(ep)) {
|
||||
dev_err(&pmcdev->pdev->dev,
|
||||
"pmc_core: couldn't get DMU telem endpoint %ld",
|
||||
PTR_ERR(ep));
|
||||
return;
|
||||
if (pmc_dev_info->dmu_guids) {
|
||||
ep = pmc_core_register_endpoint(pcidev, pmc_dev_info->dmu_guids);
|
||||
if (IS_ERR(ep)) {
|
||||
dev_err(&pmcdev->pdev->dev,
|
||||
"pmc_core: couldn't get DMU telem endpoint %ld",
|
||||
PTR_ERR(ep));
|
||||
return;
|
||||
}
|
||||
|
||||
pmcdev->punit_ep = ep;
|
||||
pmcdev->die_c6_offset = pmc_dev_info->die_c6_offset;
|
||||
}
|
||||
|
||||
pmcdev->punit_ep = ep;
|
||||
pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET;
|
||||
if (pmc_dev_info->pc_guid) {
|
||||
ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc_dev_info->pc_guid, 0);
|
||||
if (IS_ERR(ep)) {
|
||||
dev_err(&pmcdev->pdev->dev,
|
||||
"pmc_core: couldn't get Package C-state telem endpoint %ld",
|
||||
PTR_ERR(ep));
|
||||
return;
|
||||
}
|
||||
|
||||
pmcdev->pc_ep = ep;
|
||||
pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters;
|
||||
pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset;
|
||||
pmcdev->pkgc_blocker_counters = pmc_dev_info->pkgc_blocker_counters;
|
||||
pmcdev->pkgc_blocker_offset = pmc_dev_info->pkgc_blocker_offset;
|
||||
}
|
||||
}
|
||||
|
||||
void pmc_core_set_device_d3(unsigned int device)
|
||||
|
|
@ -1467,6 +1523,16 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info
|
|||
pmcdev->dbgfs_dir, pmcdev,
|
||||
&pmc_core_die_c6_us_fops);
|
||||
}
|
||||
|
||||
if (pmcdev->pc_ep) {
|
||||
debugfs_create_file("pkgc_ltr_blocker_show", 0444,
|
||||
pmcdev->dbgfs_dir, pmcdev,
|
||||
&pmc_core_pkgc_ltr_blocker_fops);
|
||||
debugfs_create_file("pkgc_blocker_residency_show", 0444,
|
||||
pmcdev->dbgfs_dir, pmcdev,
|
||||
&pmc_core_pkgc_blocker_residency_fops);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1581,17 +1647,13 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
|
|||
|
||||
static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
|
||||
{
|
||||
struct pci_dev *pcidev __free(pci_dev_put) = NULL;
|
||||
struct telem_endpoint *ep;
|
||||
unsigned int pmc_idx;
|
||||
int ret;
|
||||
|
||||
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func));
|
||||
if (!pcidev)
|
||||
return -ENODEV;
|
||||
|
||||
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
|
||||
struct pmc *pmc;
|
||||
u16 devid;
|
||||
|
||||
pmc = pmcdev->pmcs[pmc_idx];
|
||||
if (!pmc)
|
||||
|
|
@ -1600,6 +1662,16 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *
|
|||
if (!pmc->map->lpm_req_guid)
|
||||
return -ENXIO;
|
||||
|
||||
if (pmc_dev_info->ssram_hidden)
|
||||
devid = pmcdev->pmcs[PMC_IDX_MAIN]->devid;
|
||||
else
|
||||
devid = pmc->devid;
|
||||
|
||||
struct pci_dev *pcidev __free(pci_dev_put) =
|
||||
pci_get_device(PCI_VENDOR_ID_INTEL, devid, NULL);
|
||||
if (!pcidev)
|
||||
return -ENODEV;
|
||||
|
||||
ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc->map->lpm_req_guid, 0);
|
||||
if (IS_ERR(ep)) {
|
||||
dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep);
|
||||
|
|
@ -1650,6 +1722,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx)
|
|||
|
||||
pmc->map = map;
|
||||
pmc->base_addr = pmc_ssram_telemetry.base_addr;
|
||||
pmc->devid = pmc_ssram_telemetry.devid;
|
||||
pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length);
|
||||
|
||||
if (!pmc->regbase) {
|
||||
|
|
@ -1662,16 +1735,17 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev)
|
||||
static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev, u8 num_pmcs, const u8 *pmc_list)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ret = pmc_core_pmc_add(pmcdev, PMC_IDX_MAIN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pmc_core_pmc_add(pmcdev, PMC_IDX_IOE);
|
||||
pmc_core_pmc_add(pmcdev, PMC_IDX_PCH);
|
||||
for (i = 0; i < num_pmcs; ++i) {
|
||||
/* Non-MAIN PMCs are allowed to fail */
|
||||
ret = pmc_core_pmc_add(pmcdev, pmc_list[i]);
|
||||
if (ret && (pmc_list[i] == PMC_IDX_MAIN))
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1693,7 +1767,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
|
|||
ssram = pmc_dev_info->regmap_list != NULL;
|
||||
if (ssram) {
|
||||
pmcdev->regmap_list = pmc_dev_info->regmap_list;
|
||||
ret = pmc_core_ssram_get_reg_base(pmcdev);
|
||||
ret = pmc_core_ssram_get_reg_base(pmcdev,
|
||||
pmc_dev_info->num_pmcs,
|
||||
pmc_dev_info->pmc_list);
|
||||
/*
|
||||
* EAGAIN error code indicates Intel PMC SSRAM Telemetry driver
|
||||
* has not finished probe and PMC info is not available yet. Try
|
||||
|
|
@ -1717,8 +1793,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
|
|||
}
|
||||
|
||||
pmc_core_get_low_power_modes(pmcdev);
|
||||
if (pmc_dev_info->dmu_guids)
|
||||
pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids);
|
||||
if (pmc_dev_info->dmu_guids || pmc_dev_info->pc_guid)
|
||||
pmc_core_punit_pmt_init(pmcdev, pmc_dev_info);
|
||||
|
||||
if (ssram) {
|
||||
ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info);
|
||||
|
|
@ -1739,6 +1815,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
|
|||
if (pmcdev->punit_ep)
|
||||
pmt_telem_unregister_endpoint(pmcdev->punit_ep);
|
||||
|
||||
if (pmcdev->pc_ep)
|
||||
pmt_telem_unregister_endpoint(pmcdev->pc_ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1771,6 +1850,8 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
|
|||
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev),
|
||||
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev),
|
||||
X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev),
|
||||
X86_MATCH_VFM(INTEL_NOVALAKE, &nvl_s_pmc_dev),
|
||||
X86_MATCH_VFM(INTEL_NOVALAKE_L, &nvl_h_pmc_dev),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
@ -1835,6 +1916,9 @@ static void pmc_core_clean_structure(struct platform_device *pdev)
|
|||
if (pmcdev->punit_ep)
|
||||
pmt_telem_unregister_endpoint(pmcdev->punit_ep);
|
||||
|
||||
if (pmcdev->pc_ep)
|
||||
pmt_telem_unregister_endpoint(pmcdev->pc_ep);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,15 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
struct telem_endpoint;
|
||||
|
||||
DEFINE_FREE(pmc_acpi_free, void *, if (_T) ACPI_FREE(_T))
|
||||
|
||||
#define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0)
|
||||
|
||||
#define PMC_BASE_ADDR_DEFAULT 0xFE000000
|
||||
|
|
@ -46,7 +51,7 @@ struct telem_endpoint;
|
|||
#define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x68
|
||||
#define PMC_BASE_ADDR_MASK ~(SPT_PMC_MMIO_REG_LEN - 1)
|
||||
#define MTPMC_MASK 0xffff0000
|
||||
#define PPFEAR_MAX_NUM_ENTRIES 12
|
||||
#define PPFEAR_MAX_NUM_ENTRIES 13
|
||||
#define SPT_PPFEAR_NUM_ENTRIES 5
|
||||
#define SPT_PMC_READ_DISABLE_BIT 0x16
|
||||
#define SPT_PMC_MSG_FULL_STS_BIT 0x18
|
||||
|
|
@ -307,6 +312,29 @@ enum ppfear_regs {
|
|||
#define WCL_NUM_S0IX_BLOCKER 94
|
||||
#define WCL_BLK_REQ_OFFSET 50
|
||||
|
||||
/* Nova Lake */
|
||||
#define NVL_PCDH_PPFEAR_NUM_ENTRIES 13
|
||||
#define NVL_PCDH_PMC_MMIO_REG_LEN 0x363c
|
||||
#define NVL_PCDS_PMC_MMIO_REG_LEN 0x3118
|
||||
#define NVL_PCHS_PMC_MMIO_REG_LEN 0x30d8
|
||||
#define NVL_LPM_PRI_OFFSET 0x17a4
|
||||
#define NVL_LPM_EN_OFFSET 0x17a0
|
||||
#define NVL_LPM_RESIDENCY_OFFSET 0x17a8
|
||||
#define NVL_LPM_LIVE_STATUS_OFFSET 0x1760
|
||||
#define NVL_LPM_NUM_MAPS 15
|
||||
#define NVL_PCDH_NUM_S0IX_BLOCKER 107
|
||||
#define NVL_PCDS_NUM_S0IX_BLOCKER 71
|
||||
#define NVL_PCHS_NUM_S0IX_BLOCKER 54
|
||||
#define NVL_PCDS_PMC_LTR_RESERVED 0x1bac
|
||||
#define NVL_PCDH_BLK_REQ_OFFSET 53
|
||||
#define NVL_PCDS_BLK_REQ_OFFSET 18
|
||||
#define NVL_PCHS_BLK_REQ_OFFSET 46
|
||||
#define NVL_PMT_PC_GUID 0x13000101
|
||||
#define NVL_PMT_DMU_GUID 0x1a000101
|
||||
#define NVL_LTR_BLK_OFFSET 64
|
||||
#define NVL_PKGC_BLK_OFFSET 4
|
||||
#define NVL_PMT_DMU_DIE_C6_OFFSET 25
|
||||
|
||||
/* SSRAM PMC Device ID */
|
||||
/* LNL */
|
||||
#define PMC_DEVID_LNL_SOCM 0xa87f
|
||||
|
|
@ -329,6 +357,11 @@ enum ppfear_regs {
|
|||
#define PMC_DEVID_MTL_IOEP 0x7ecf
|
||||
#define PMC_DEVID_MTL_IOEM 0x7ebf
|
||||
|
||||
/* NVL */
|
||||
#define PMC_DEVID_NVL_PCDH 0xd37e
|
||||
#define PMC_DEVID_NVL_PCDS 0xd47e
|
||||
#define PMC_DEVID_NVL_PCHS 0x6e27
|
||||
|
||||
extern const char *pmc_lpm_modes[];
|
||||
|
||||
struct pmc_bit_map {
|
||||
|
|
@ -425,6 +458,7 @@ struct pmc_info {
|
|||
* @ltr_ign: Holds LTR ignore data while suspended
|
||||
* @num_lpm_modes: Count of enabled modes
|
||||
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
|
||||
* @devid: Device ID of the SSRAM device
|
||||
*
|
||||
* pmc contains info about one power management controller device.
|
||||
*/
|
||||
|
|
@ -436,6 +470,7 @@ struct pmc {
|
|||
u32 ltr_ign;
|
||||
u8 num_lpm_modes;
|
||||
u8 lpm_en_modes[LPM_MAX_NUM_MODES];
|
||||
u16 devid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -453,6 +488,11 @@ struct pmc {
|
|||
* @suspend: Function to perform platform specific suspend
|
||||
* @resume: Function to perform platform specific resume
|
||||
*
|
||||
* @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters
|
||||
* @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
|
||||
* @pkgc_blocker_counters: Array of PKGC blocker counters
|
||||
* @pkgc_blocker_offset: Offset to PKGC blocker in telemetry region
|
||||
*
|
||||
* pmc_dev contains info about power management controller device.
|
||||
*/
|
||||
struct pmc_dev {
|
||||
|
|
@ -471,8 +511,14 @@ struct pmc_dev {
|
|||
u8 num_of_pkgc;
|
||||
|
||||
u32 die_c6_offset;
|
||||
struct telem_endpoint *pc_ep;
|
||||
struct telem_endpoint *punit_ep;
|
||||
struct pmc_info *regmap_list;
|
||||
|
||||
const char **pkgc_ltr_blocker_counters;
|
||||
u32 pkgc_ltr_blocker_offset;
|
||||
const char **pkgc_blocker_counters;
|
||||
u32 pkgc_blocker_offset;
|
||||
};
|
||||
|
||||
enum pmc_index {
|
||||
|
|
@ -484,29 +530,45 @@ enum pmc_index {
|
|||
|
||||
/**
|
||||
* struct pmc_dev_info - Structure to keep PMC device info
|
||||
* @pci_func: Function number of the primary PMC
|
||||
* @dmu_guids: List of Die Management Unit GUID
|
||||
* @pc_guid: GUID for telemetry region to read PKGC blocker info
|
||||
* @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
|
||||
* @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region
|
||||
* @num_pmcs: Number of entries in @pmc_list
|
||||
* @pmc_list: Index list of available PMC
|
||||
* @regmap_list: Pointer to a list of pmc_info structure that could be
|
||||
* available for the platform. When set, this field implies
|
||||
* SSRAM support.
|
||||
* @map: Pointer to a pmc_reg_map struct that contains platform
|
||||
* specific attributes of the primary PMC
|
||||
* @sub_req_show: File operations to show substate requirements
|
||||
* @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters
|
||||
* @pkgc_blocker_counters: Array of PKGC blocker counters
|
||||
* @suspend: Function to perform platform specific suspend
|
||||
* @resume: Function to perform platform specific resume
|
||||
* @init: Function to perform platform specific init action
|
||||
* @sub_req: Function to achieve low power mode substate requirements
|
||||
* @ssram_hidden: Some SSRAM devices are hidden on this platform
|
||||
* @die_c6_offset: Telemetry offset to read Die C6 residency
|
||||
*/
|
||||
struct pmc_dev_info {
|
||||
u8 pci_func;
|
||||
u32 *dmu_guids;
|
||||
u32 pc_guid;
|
||||
u32 pkgc_ltr_blocker_offset;
|
||||
u32 pkgc_blocker_offset;
|
||||
u8 num_pmcs;
|
||||
const u8 *pmc_list;
|
||||
struct pmc_info *regmap_list;
|
||||
const struct pmc_reg_map *map;
|
||||
const struct file_operations *sub_req_show;
|
||||
const char **pkgc_ltr_blocker_counters;
|
||||
const char **pkgc_blocker_counters;
|
||||
void (*suspend)(struct pmc_dev *pmcdev);
|
||||
int (*resume)(struct pmc_dev *pmcdev);
|
||||
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
|
||||
int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
|
||||
bool ssram_hidden;
|
||||
u32 die_c6_offset;
|
||||
};
|
||||
|
||||
extern const struct pmc_bit_map msr_map[];
|
||||
|
|
@ -529,13 +591,14 @@ extern const struct pmc_reg_map mtl_ioep_reg_map;
|
|||
extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[];
|
||||
extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[];
|
||||
extern const struct pmc_bit_map ptl_pcdp_signal_status_map[];
|
||||
extern const struct pmc_bit_map ptl_pcdp_ltr_show_map[];
|
||||
|
||||
void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
|
||||
int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
|
||||
|
||||
int pmc_core_resume_common(struct pmc_dev *pmcdev);
|
||||
int get_primary_reg_base(struct pmc *pmc);
|
||||
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids);
|
||||
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
|
||||
void pmc_core_set_device_d3(unsigned int device);
|
||||
|
||||
int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
|
||||
|
|
@ -552,6 +615,8 @@ extern struct pmc_dev_info arl_h_pmc_dev;
|
|||
extern struct pmc_dev_info lnl_pmc_dev;
|
||||
extern struct pmc_dev_info ptl_pmc_dev;
|
||||
extern struct pmc_dev_info wcl_pmc_dev;
|
||||
extern struct pmc_dev_info nvl_s_pmc_dev;
|
||||
extern struct pmc_dev_info nvl_h_pmc_dev;
|
||||
|
||||
void cnl_suspend(struct pmc_dev *pmcdev);
|
||||
int cnl_resume(struct pmc_dev *pmcdev);
|
||||
|
|
@ -562,6 +627,8 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
|
|||
extern const struct file_operations pmc_core_substate_req_regs_fops;
|
||||
extern const struct file_operations pmc_core_substate_blk_req_fops;
|
||||
|
||||
extern const guid_t intel_vsec_guid;
|
||||
|
||||
#define pmc_for_each_mode(mode, pmc) \
|
||||
for (unsigned int __i = 0, __cond; \
|
||||
__cond = __i < (pmc)->num_lpm_modes, \
|
||||
|
|
@ -583,4 +650,13 @@ static const struct file_operations __name ## _fops = { \
|
|||
.release = single_release, \
|
||||
}
|
||||
|
||||
struct intel_vsec_header;
|
||||
union acpi_object;
|
||||
|
||||
/* Avoid checkpatch warning */
|
||||
typedef u32 (*acpi_disc_t)[PMT_DISC_DWORDS];
|
||||
|
||||
acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj,
|
||||
struct intel_vsec_header *header);
|
||||
union acpi_object *pmc_find_telem_guid(union acpi_object *dsd);
|
||||
#endif /* PMC_CORE_H */
|
||||
|
|
|
|||
|
|
@ -544,6 +544,8 @@ static struct pmc_info lnl_pmc_info_list[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const u8 lnl_pmc_list[] = {PMC_IDX_MAIN};
|
||||
|
||||
#define LNL_NPU_PCI_DEV 0x643e
|
||||
#define LNL_IPU_PCI_DEV 0x645d
|
||||
|
||||
|
|
@ -571,7 +573,8 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
|
|||
}
|
||||
|
||||
struct pmc_dev_info lnl_pmc_dev = {
|
||||
.pci_func = 2,
|
||||
.num_pmcs = ARRAY_SIZE(lnl_pmc_list),
|
||||
.pmc_list = lnl_pmc_list,
|
||||
.regmap_list = lnl_pmc_info_list,
|
||||
.map = &lnl_socm_reg_map,
|
||||
.sub_req_show = &pmc_core_substate_req_regs_fops,
|
||||
|
|
@ -579,4 +582,5 @@ struct pmc_dev_info lnl_pmc_dev = {
|
|||
.resume = lnl_resume,
|
||||
.init = lnl_core_init,
|
||||
.sub_req = pmc_core_pmt_get_lpm_req,
|
||||
.ssram_hidden = true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -965,6 +965,8 @@ static struct pmc_info mtl_pmc_info_list[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const u8 mtl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE};
|
||||
|
||||
#define MTL_GNA_PCI_DEV 0x7e4c
|
||||
#define MTL_IPU_PCI_DEV 0x7d19
|
||||
#define MTL_VPU_PCI_DEV 0x7d1d
|
||||
|
|
@ -994,8 +996,9 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
|
|||
|
||||
static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0};
|
||||
struct pmc_dev_info mtl_pmc_dev = {
|
||||
.pci_func = 2,
|
||||
.dmu_guids = MTL_PMT_DMU_GUIDS,
|
||||
.num_pmcs = ARRAY_SIZE(mtl_pmc_list),
|
||||
.pmc_list = mtl_pmc_list,
|
||||
.regmap_list = mtl_pmc_info_list,
|
||||
.map = &mtl_socm_reg_map,
|
||||
.sub_req_show = &pmc_core_substate_req_regs_fops,
|
||||
|
|
@ -1003,4 +1006,6 @@ struct pmc_dev_info mtl_pmc_dev = {
|
|||
.resume = mtl_resume,
|
||||
.init = mtl_core_init,
|
||||
.sub_req = pmc_core_pmt_get_lpm_req,
|
||||
.ssram_hidden = true,
|
||||
.die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET,
|
||||
};
|
||||
|
|
|
|||
1539
drivers/platform/x86/intel/pmc/nvl.c
Normal file
1539
drivers/platform/x86/intel/pmc/nvl.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -137,7 +137,7 @@ static const struct pmc_bit_map *ext_ptl_pcdp_pfear_map[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = {
|
||||
const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = {
|
||||
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
|
||||
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
|
||||
{"SATA", CNP_PMC_LTR_SATA},
|
||||
|
|
@ -543,6 +543,8 @@ static struct pmc_info ptl_pmc_info_list[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const u8 ptl_pmc_list[] = {PMC_IDX_MAIN};
|
||||
|
||||
#define PTL_NPU_PCI_DEV 0xb03e
|
||||
#define PTL_IPU_PCI_DEV 0xb05d
|
||||
|
||||
|
|
@ -569,7 +571,8 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
|
|||
}
|
||||
|
||||
struct pmc_dev_info ptl_pmc_dev = {
|
||||
.pci_func = 2,
|
||||
.num_pmcs = ARRAY_SIZE(ptl_pmc_list),
|
||||
.pmc_list = ptl_pmc_list,
|
||||
.regmap_list = ptl_pmc_info_list,
|
||||
.map = &ptl_pcdp_reg_map,
|
||||
.sub_req_show = &pmc_core_substate_blk_req_fops,
|
||||
|
|
@ -577,4 +580,5 @@ struct pmc_dev_info ptl_pmc_dev = {
|
|||
.resume = ptl_resume,
|
||||
.init = ptl_core_init,
|
||||
.sub_req = pmc_core_pmt_get_blk_sub_req,
|
||||
.ssram_hidden = true,
|
||||
};
|
||||
|
|
|
|||
216
drivers/platform/x86/intel/pmc/pwrm_telemetry.c
Normal file
216
drivers/platform/x86/intel/pmc/pwrm_telemetry.c
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Intel PMC PWRM ACPI driver
|
||||
*
|
||||
* Copyright (C) 2025, Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#define ENTRY_LEN 5
|
||||
|
||||
/* DWORD2 */
|
||||
#define DVSEC_ID_MASK GENMASK(15, 0)
|
||||
#define NUM_ENTRIES_MASK GENMASK(23, 16)
|
||||
#define ENTRY_SIZE_MASK GENMASK(31, 24)
|
||||
|
||||
/* DWORD3 */
|
||||
#define TBIR_MASK GENMASK(2, 0)
|
||||
#define DISC_TBL_OFF_MASK GENMASK(31, 3)
|
||||
|
||||
const guid_t intel_vsec_guid =
|
||||
GUID_INIT(0x294903fb, 0x634d, 0x4fc7, 0xaf, 0x1f, 0x0f, 0xb9,
|
||||
0x56, 0xb0, 0x4f, 0xc1);
|
||||
|
||||
static bool is_valid_entry(union acpi_object *pkg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!pkg || pkg->type != ACPI_TYPE_PACKAGE || pkg->package.count != ENTRY_LEN)
|
||||
return false;
|
||||
|
||||
if (pkg->package.elements[0].type != ACPI_TYPE_STRING)
|
||||
return false;
|
||||
|
||||
for (i = 1; i < ENTRY_LEN; i++)
|
||||
if (pkg->package.elements[i].type != ACPI_TYPE_INTEGER)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj,
|
||||
struct intel_vsec_header *header)
|
||||
{
|
||||
union acpi_object *vsec_pkg;
|
||||
union acpi_object *disc_pkg;
|
||||
u64 hdr0, hdr1;
|
||||
int num_regions;
|
||||
int i;
|
||||
|
||||
if (!header)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* First Package is DVSEC info */
|
||||
vsec_pkg = &obj->package.elements[0];
|
||||
if (!is_valid_entry(vsec_pkg))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
hdr0 = vsec_pkg->package.elements[3].integer.value;
|
||||
hdr1 = vsec_pkg->package.elements[4].integer.value;
|
||||
|
||||
header->id = FIELD_GET(DVSEC_ID_MASK, hdr0);
|
||||
header->num_entries = FIELD_GET(NUM_ENTRIES_MASK, hdr0);
|
||||
header->entry_size = FIELD_GET(ENTRY_SIZE_MASK, hdr0);
|
||||
header->tbir = FIELD_GET(TBIR_MASK, hdr1);
|
||||
header->offset = hdr1 & DISC_TBL_OFF_MASK;
|
||||
|
||||
/* Second Package contains the discovery tables */
|
||||
disc_pkg = &obj->package.elements[1];
|
||||
if (disc_pkg->type != ACPI_TYPE_PACKAGE || disc_pkg->package.count < 1)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
num_regions = disc_pkg->package.count;
|
||||
if (header->num_entries != num_regions)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
acpi_disc_t disc __free(kfree) = kmalloc_array(num_regions, sizeof(*disc),
|
||||
GFP_KERNEL);
|
||||
if (!disc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < num_regions; i++) {
|
||||
union acpi_object *pkg;
|
||||
u64 value;
|
||||
int j;
|
||||
|
||||
pkg = &disc_pkg->package.elements[i];
|
||||
if (!is_valid_entry(pkg))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* Element 0 is a descriptive string; DWORD values start at index 1. */
|
||||
for (j = 1; j < ENTRY_LEN; j++) {
|
||||
value = pkg->package.elements[j].integer.value;
|
||||
if (value > U32_MAX)
|
||||
return ERR_PTR(-ERANGE);
|
||||
|
||||
disc[i][j - 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return no_free_ptr(disc);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmc_parse_telem_dsd, "INTEL_PMC_CORE");
|
||||
|
||||
union acpi_object *pmc_find_telem_guid(union acpi_object *dsd)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dsd || dsd->type != ACPI_TYPE_PACKAGE)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i + 1 < dsd->package.count; i += 2) {
|
||||
union acpi_object *uuid_obj, *data_obj;
|
||||
guid_t uuid;
|
||||
|
||||
uuid_obj = &dsd->package.elements[i];
|
||||
data_obj = &dsd->package.elements[i + 1];
|
||||
|
||||
if (uuid_obj->type != ACPI_TYPE_BUFFER ||
|
||||
uuid_obj->buffer.length != 16)
|
||||
continue;
|
||||
|
||||
memcpy(&uuid, uuid_obj->buffer.pointer, 16);
|
||||
if (guid_equal(&uuid, &intel_vsec_guid))
|
||||
return data_obj;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmc_find_telem_guid, "INTEL_PMC_CORE");
|
||||
|
||||
static int pmc_pwrm_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_vsec_header header;
|
||||
struct intel_vsec_header *headers[2] = { &header, NULL };
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct intel_vsec_platform_info info = { };
|
||||
struct device *dev = &pdev->dev;
|
||||
union acpi_object *dsd;
|
||||
struct resource *res;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
|
||||
handle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_DSD", NULL, &buf);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
return dev_err_probe(dev, -ENODEV, "Could not evaluate _DSD: %s\n",
|
||||
acpi_format_exception(status));
|
||||
}
|
||||
|
||||
void *dsd_buf __free(pmc_acpi_free) = buf.pointer;
|
||||
|
||||
dsd = pmc_find_telem_guid(dsd_buf);
|
||||
if (!dsd)
|
||||
return -ENODEV;
|
||||
|
||||
acpi_disc_t acpi_disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header);
|
||||
if (IS_ERR(acpi_disc))
|
||||
return PTR_ERR(acpi_disc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, header.tbir);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
info.headers = headers;
|
||||
info.caps = VSEC_CAP_TELEMETRY;
|
||||
info.acpi_disc = acpi_disc;
|
||||
info.src = INTEL_VSEC_DISC_ACPI;
|
||||
info.base_addr = res->start;
|
||||
|
||||
return intel_vsec_register(&pdev->dev, &info);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id pmc_pwrm_acpi_ids[] = {
|
||||
{ "INTC1122", 0 }, /* Nova Lake */
|
||||
{ "INTC1129", 0 }, /* Nova Lake */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pmc_pwrm_acpi_ids);
|
||||
|
||||
static struct platform_driver pmc_pwrm_acpi_driver = {
|
||||
.probe = pmc_pwrm_acpi_probe,
|
||||
.driver = {
|
||||
.name = "intel_pmc_pwrm_acpi",
|
||||
.acpi_match_table = ACPI_PTR(pmc_pwrm_acpi_ids),
|
||||
},
|
||||
};
|
||||
module_platform_driver(pmc_pwrm_acpi_driver);
|
||||
|
||||
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel PMC PWRM ACPI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("INTEL_VSEC");
|
||||
|
|
@ -5,7 +5,11 @@
|
|||
* Copyright (c) 2023, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/types.h>
|
||||
|
|
@ -21,11 +25,114 @@
|
|||
#define SSRAM_PCH_OFFSET 0x60
|
||||
#define SSRAM_IOE_OFFSET 0x68
|
||||
#define SSRAM_DEVID_OFFSET 0x70
|
||||
#define SSRAM_BASE_ADDR_MASK GENMASK_ULL(63, 3)
|
||||
#define SSRAM_PCI_PMC_MASK (BIT(PMC_IDX_MAIN) | BIT(PMC_IDX_IOE) | BIT(PMC_IDX_PCH))
|
||||
|
||||
DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T))
|
||||
|
||||
static struct pmc_ssram_telemetry *pmc_ssram_telems;
|
||||
static bool device_probed;
|
||||
enum resource_method {
|
||||
RES_METHOD_PCI,
|
||||
RES_METHOD_ACPI,
|
||||
};
|
||||
|
||||
struct ssram_type {
|
||||
enum resource_method method;
|
||||
enum pmc_index p_index;
|
||||
};
|
||||
|
||||
static const struct ssram_type pci_main = {
|
||||
.method = RES_METHOD_PCI,
|
||||
.p_index = PMC_IDX_MAIN,
|
||||
};
|
||||
|
||||
static const struct ssram_type acpi_main = {
|
||||
.method = RES_METHOD_ACPI,
|
||||
.p_index = PMC_IDX_MAIN,
|
||||
};
|
||||
|
||||
static const struct ssram_type acpi_pch = {
|
||||
.method = RES_METHOD_ACPI,
|
||||
.p_index = PMC_IDX_PCH,
|
||||
};
|
||||
|
||||
enum pmc_ssram_state {
|
||||
PMC_SSRAM_UNPROBED = 0,
|
||||
PMC_SSRAM_PROBING,
|
||||
PMC_SSRAM_PRESENT,
|
||||
PMC_SSRAM_ABSENT,
|
||||
};
|
||||
|
||||
static enum pmc_ssram_state pmc_ssram_state[MAX_NUM_PMC];
|
||||
static struct pmc_ssram_telemetry pmc_ssram_telems[MAX_NUM_PMC];
|
||||
|
||||
struct pmc_ssram_probe_cache {
|
||||
/* Per-index values staged by probe before publishing globally. */
|
||||
struct pmc_ssram_telemetry telems[MAX_NUM_PMC];
|
||||
/* PMCs this probe instance is responsible for publishing. */
|
||||
unsigned long owned_mask;
|
||||
/* Subset of owned_mask that was discovered successfully. */
|
||||
unsigned long valid_mask;
|
||||
};
|
||||
|
||||
struct pmc_ssram_drvdata {
|
||||
/* PMCs published by this bound device; used to unpublish on remove. */
|
||||
unsigned long owned_mask;
|
||||
};
|
||||
|
||||
static inline u64 get_base(void __iomem *addr, u32 offset)
|
||||
{
|
||||
return lo_hi_readq(addr + offset) & SSRAM_BASE_ADDR_MASK;
|
||||
}
|
||||
|
||||
static void pmc_ssram_get_devid_pwrmbase(struct pmc_ssram_probe_cache *probe_cache,
|
||||
void __iomem *ssram, unsigned int pmc_idx)
|
||||
{
|
||||
u64 pwrm_base;
|
||||
u16 devid;
|
||||
|
||||
pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
|
||||
devid = readw(ssram + SSRAM_DEVID_OFFSET);
|
||||
|
||||
probe_cache->telems[pmc_idx].base_addr = pwrm_base;
|
||||
probe_cache->telems[pmc_idx].devid = devid;
|
||||
}
|
||||
|
||||
static void pmc_ssram_publish_absent(unsigned int pmc_idx)
|
||||
{
|
||||
/*
|
||||
* Publish only the state without modifying base_addr and devid. This
|
||||
* lets a reader that already observed PRESENT finish copying the
|
||||
* previous values even if unbind concurrently publishes ABSENT. Readers
|
||||
* that observe ABSENT return -ENODEV without accessing data.
|
||||
*/
|
||||
smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_ABSENT);
|
||||
}
|
||||
|
||||
static void pmc_ssram_publish_present(struct pmc_ssram_probe_cache *probe_cache,
|
||||
unsigned int pmc_idx)
|
||||
{
|
||||
/*
|
||||
* The devid and base_addr fields are read from hardware MMIO registers
|
||||
* whose values are stable for a given PMC index. A reader that observed
|
||||
* PRESENT from an earlier probe can safely copy them while a concurrent
|
||||
* rebind republishes those fields because both probes read the same
|
||||
* hardware values.
|
||||
*/
|
||||
pmc_ssram_telems[pmc_idx] = probe_cache->telems[pmc_idx];
|
||||
/*
|
||||
* Publish base_addr and devid before publishing PRESENT. Pairs with the
|
||||
* acquire load in the reader that consumes them after observing PRESENT.
|
||||
*/
|
||||
smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_PRESENT);
|
||||
}
|
||||
|
||||
static void pmc_ssram_mark_probing(unsigned long mask)
|
||||
{
|
||||
unsigned long bit;
|
||||
|
||||
for_each_set_bit(bit, &mask, MAX_NUM_PMC)
|
||||
WRITE_ONCE(pmc_ssram_state[bit], PMC_SSRAM_PROBING);
|
||||
}
|
||||
|
||||
static int
|
||||
pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram)
|
||||
|
|
@ -63,18 +170,15 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem
|
|||
return intel_vsec_register(&pcidev->dev, &info);
|
||||
}
|
||||
|
||||
static inline u64 get_base(void __iomem *addr, u32 offset)
|
||||
{
|
||||
return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3);
|
||||
}
|
||||
|
||||
static int
|
||||
pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset)
|
||||
pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev,
|
||||
struct pmc_ssram_probe_cache *probe_cache,
|
||||
unsigned int pmc_idx, u32 offset)
|
||||
{
|
||||
void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL;
|
||||
void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL;
|
||||
u64 ssram_base, pwrm_base;
|
||||
u16 devid;
|
||||
u64 ssram_base;
|
||||
int ret;
|
||||
|
||||
ssram_base = pci_resource_start(pcidev, 0);
|
||||
tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
|
||||
|
|
@ -99,14 +203,107 @@ pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 of
|
|||
ssram = no_free_ptr(tmp_ssram);
|
||||
}
|
||||
|
||||
pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
|
||||
devid = readw(ssram + SSRAM_DEVID_OFFSET);
|
||||
|
||||
pmc_ssram_telems[pmc_idx].devid = devid;
|
||||
pmc_ssram_telems[pmc_idx].base_addr = pwrm_base;
|
||||
pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx);
|
||||
|
||||
/* Find and register and PMC telemetry entries */
|
||||
return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram);
|
||||
ret = pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram);
|
||||
if (ret)
|
||||
dev_warn(&pcidev->dev, "could not register PMT\n");
|
||||
|
||||
probe_cache->valid_mask |= BIT(pmc_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev,
|
||||
struct pmc_ssram_probe_cache *probe_cache)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_MAIN, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If MAIN PMC enumeration is successful but either IOE or PCH fail,
|
||||
* don't fail probe as the MAIN PMC is still useful as it provides the
|
||||
* global reset and slp_s0 counter access. Failed or missing secondary
|
||||
* PMCs are left out of valid_mask and published as absent.
|
||||
*/
|
||||
pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_IOE,
|
||||
SSRAM_IOE_OFFSET);
|
||||
|
||||
pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_PCH,
|
||||
SSRAM_PCH_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_ssram_telemetry_get_pmc_acpi(struct pci_dev *pcidev,
|
||||
struct pmc_ssram_probe_cache *probe_cache,
|
||||
unsigned int pmc_idx)
|
||||
{
|
||||
u64 ssram_base;
|
||||
|
||||
ssram_base = pci_resource_start(pcidev, 0);
|
||||
if (!ssram_base)
|
||||
return -ENODEV;
|
||||
|
||||
void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram =
|
||||
ioremap(ssram_base, SSRAM_HDR_SIZE);
|
||||
if (!ssram)
|
||||
return -ENOMEM;
|
||||
|
||||
pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx);
|
||||
probe_cache->valid_mask |= BIT(pmc_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_ssram_telemetry_acpi_init(struct pci_dev *pcidev,
|
||||
struct pmc_ssram_probe_cache *probe_cache,
|
||||
enum pmc_index index)
|
||||
{
|
||||
struct intel_vsec_header header;
|
||||
struct intel_vsec_header *headers[2] = { &header, NULL };
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct intel_vsec_platform_info info = { };
|
||||
union acpi_object *dsd;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(&pcidev->dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_DSD", NULL, &buf);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
void *dsd_buf __free(pmc_acpi_free) = buf.pointer;
|
||||
|
||||
dsd = pmc_find_telem_guid(dsd_buf);
|
||||
if (!dsd)
|
||||
return -ENODEV;
|
||||
|
||||
acpi_disc_t disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header);
|
||||
if (IS_ERR(disc))
|
||||
return PTR_ERR(disc);
|
||||
|
||||
info.headers = headers;
|
||||
info.caps = VSEC_CAP_TELEMETRY;
|
||||
info.acpi_disc = disc;
|
||||
info.src = INTEL_VSEC_DISC_ACPI;
|
||||
|
||||
/* This is an ACPI companion device. PCI BAR will be used for base addr. */
|
||||
info.base_addr = 0;
|
||||
|
||||
ret = intel_vsec_register(&pcidev->dev, &info);
|
||||
if (ret)
|
||||
dev_warn(&pcidev->dev, "could not register PMT\n");
|
||||
|
||||
return pmc_ssram_telemetry_get_pmc_acpi(pcidev, probe_cache, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -114,48 +311,107 @@ pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 of
|
|||
* @pmc_idx: Index of the PMC
|
||||
* @pmc_ssram_telemetry: pmc_ssram_telemetry structure to store the PMC information
|
||||
*
|
||||
* State flow per PMC index:
|
||||
* - PMC_SSRAM_UNPROBED: Probe has not started for this index.
|
||||
* - PMC_SSRAM_PROBING: Probe is currently discovering data for this index.
|
||||
* - PMC_SSRAM_PRESENT: base_addr/devid were published and can be read.
|
||||
* - PMC_SSRAM_ABSENT: No data is available for this index.
|
||||
*
|
||||
* Readers use an acquire load of the state. If state is PRESENT, reads of
|
||||
* base_addr/devid are ordered after the state observation and pair with the
|
||||
* writer's release-store when publishing PRESENT.
|
||||
*
|
||||
* Return:
|
||||
* * 0 - Success
|
||||
* * -EAGAIN - Probe function has not finished yet. Try again.
|
||||
* * -EINVAL - Invalid pmc_idx
|
||||
* * -ENODEV - PMC device is not available
|
||||
* * -ENODEV - PMC device is not available (hardware absent or driver failed to initialize)
|
||||
*/
|
||||
int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx,
|
||||
struct pmc_ssram_telemetry *pmc_ssram_telemetry)
|
||||
{
|
||||
/*
|
||||
* PMCs are discovered in probe function. If this function is called before
|
||||
* probe function complete, the result would be invalid. Use device_probed
|
||||
* variable to avoid this case. Return -EAGAIN to inform the consumer to call
|
||||
* again later.
|
||||
*/
|
||||
if (!device_probed)
|
||||
return -EAGAIN;
|
||||
enum pmc_ssram_state state;
|
||||
|
||||
/*
|
||||
* Memory barrier is used to ensure the correct read order between
|
||||
* device_probed variable and PMC info.
|
||||
*/
|
||||
smp_rmb();
|
||||
if (pmc_idx >= MAX_NUM_PMC)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pmc_ssram_telems || !pmc_ssram_telems[pmc_idx].devid)
|
||||
/*
|
||||
* PMCs are discovered in probe function. If this function is called before
|
||||
* probe function complete, the result would be invalid. Use per-PMC state
|
||||
* to inform the consumer to call again later.
|
||||
*/
|
||||
state = smp_load_acquire(&pmc_ssram_state[pmc_idx]);
|
||||
if (state == PMC_SSRAM_UNPROBED || state == PMC_SSRAM_PROBING)
|
||||
return -EAGAIN;
|
||||
|
||||
if (state == PMC_SSRAM_ABSENT)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Acquire semantics order reads of devid and base_addr after observing
|
||||
* PRESENT and pair with the writer's release-store.
|
||||
*/
|
||||
pmc_ssram_telemetry->devid = pmc_ssram_telems[pmc_idx].devid;
|
||||
pmc_ssram_telemetry->base_addr = pmc_ssram_telems[pmc_idx].base_addr;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info);
|
||||
|
||||
static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id)
|
||||
static void pmc_ssram_publish_absent_mask(unsigned long mask)
|
||||
{
|
||||
unsigned long bit;
|
||||
|
||||
for_each_set_bit(bit, &mask, MAX_NUM_PMC)
|
||||
pmc_ssram_publish_absent(bit);
|
||||
}
|
||||
|
||||
static void pmc_ssram_publish_telems(struct pmc_ssram_probe_cache *probe_cache, int ret)
|
||||
{
|
||||
unsigned long bit;
|
||||
|
||||
/* If probe failed, all owned indexes become absent for readers. */
|
||||
if (ret) {
|
||||
pmc_ssram_publish_absent_mask(probe_cache->owned_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Publish each owned index independently based on discovery result. */
|
||||
for_each_set_bit(bit, &probe_cache->owned_mask, MAX_NUM_PMC) {
|
||||
if (probe_cache->valid_mask & BIT(bit))
|
||||
pmc_ssram_publish_present(probe_cache, bit);
|
||||
else
|
||||
pmc_ssram_publish_absent(bit);
|
||||
}
|
||||
}
|
||||
|
||||
static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id)
|
||||
{
|
||||
struct pmc_ssram_probe_cache probe_cache = {};
|
||||
struct pmc_ssram_drvdata *drvdata;
|
||||
const struct ssram_type *ssram_type;
|
||||
enum resource_method method;
|
||||
enum pmc_index index;
|
||||
int ret;
|
||||
|
||||
pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC,
|
||||
GFP_KERNEL);
|
||||
if (!pmc_ssram_telems) {
|
||||
ssram_type = (const struct ssram_type *)id->driver_data;
|
||||
if (!ssram_type) {
|
||||
dev_dbg(&pcidev->dev, "missing driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
index = ssram_type->p_index;
|
||||
method = ssram_type->method;
|
||||
if (method == RES_METHOD_PCI)
|
||||
probe_cache.owned_mask = SSRAM_PCI_PMC_MASK;
|
||||
else if (method == RES_METHOD_ACPI)
|
||||
probe_cache.owned_mask = BIT(index);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
pmc_ssram_mark_probing(probe_cache.owned_mask);
|
||||
|
||||
drvdata = devm_kzalloc(&pcidev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata) {
|
||||
ret = -ENOMEM;
|
||||
goto probe_finish;
|
||||
}
|
||||
|
|
@ -166,43 +422,67 @@ static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct
|
|||
goto probe_finish;
|
||||
}
|
||||
|
||||
ret = pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_MAIN, 0);
|
||||
if (ret)
|
||||
goto probe_finish;
|
||||
if (method == RES_METHOD_PCI)
|
||||
ret = pmc_ssram_telemetry_pci_init(pcidev, &probe_cache);
|
||||
else if (method == RES_METHOD_ACPI)
|
||||
ret = pmc_ssram_telemetry_acpi_init(pcidev, &probe_cache, index);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET);
|
||||
pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET);
|
||||
if (!ret) {
|
||||
drvdata->owned_mask = probe_cache.owned_mask;
|
||||
pci_set_drvdata(pcidev, drvdata);
|
||||
}
|
||||
|
||||
probe_finish:
|
||||
/*
|
||||
* Memory barrier is used to ensure the correct write order between PMC info
|
||||
* and device_probed variable.
|
||||
*/
|
||||
smp_wmb();
|
||||
device_probed = true;
|
||||
pmc_ssram_publish_telems(&probe_cache, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) },
|
||||
static void pmc_ssram_telemetry_remove(struct pci_dev *pcidev)
|
||||
{
|
||||
struct pmc_ssram_drvdata *drvdata = pci_get_drvdata(pcidev);
|
||||
|
||||
if (drvdata)
|
||||
pmc_ssram_publish_absent_mask(drvdata->owned_mask);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN),
|
||||
.driver_data = (kernel_ulong_t)&pci_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDH),
|
||||
.driver_data = (kernel_ulong_t)&acpi_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDS),
|
||||
.driver_data = (kernel_ulong_t)&acpi_main },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCHS),
|
||||
.driver_data = (kernel_ulong_t)&acpi_pch },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids);
|
||||
MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids);
|
||||
|
||||
static struct pci_driver intel_pmc_ssram_telemetry_driver = {
|
||||
static struct pci_driver pmc_ssram_telemetry_driver = {
|
||||
.name = "intel_pmc_ssram_telemetry",
|
||||
.id_table = intel_pmc_ssram_telemetry_pci_ids,
|
||||
.probe = intel_pmc_ssram_telemetry_probe,
|
||||
.id_table = pmc_ssram_telemetry_pci_ids,
|
||||
.probe = pmc_ssram_telemetry_probe,
|
||||
.remove = pmc_ssram_telemetry_remove,
|
||||
};
|
||||
module_pci_driver(intel_pmc_ssram_telemetry_driver);
|
||||
module_pci_driver(pmc_ssram_telemetry_driver);
|
||||
|
||||
MODULE_IMPORT_NS("INTEL_VSEC");
|
||||
MODULE_IMPORT_NS("INTEL_PMC_CORE");
|
||||
MODULE_AUTHOR("Xi Pardee <xi.pardee@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel PMC SSRAM Telemetry driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -469,6 +469,8 @@ static struct pmc_info wcl_pmc_info_list[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const u8 wcl_pmc_list[] = {PMC_IDX_MAIN};
|
||||
|
||||
#define WCL_NPU_PCI_DEV 0xfd3e
|
||||
|
||||
/*
|
||||
|
|
@ -493,12 +495,14 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
|
|||
}
|
||||
|
||||
struct pmc_dev_info wcl_pmc_dev = {
|
||||
.pci_func = 2,
|
||||
.regmap_list = wcl_pmc_info_list,
|
||||
.num_pmcs = ARRAY_SIZE(wcl_pmc_list),
|
||||
.pmc_list = wcl_pmc_list,
|
||||
.map = &wcl_pcdn_reg_map,
|
||||
.sub_req_show = &pmc_core_substate_blk_req_fops,
|
||||
.suspend = cnl_suspend,
|
||||
.resume = wcl_resume,
|
||||
.init = wcl_core_init,
|
||||
.sub_req = pmc_core_pmt_get_blk_sub_req,
|
||||
.ssram_hidden = true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/log2.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pci.h>
|
||||
|
|
@ -204,13 +205,14 @@ struct class intel_pmt_class = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(intel_pmt_class);
|
||||
|
||||
static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
||||
struct intel_vsec_device *ivdev,
|
||||
struct resource *disc_res)
|
||||
static int pmt_resolve_access_pci(struct intel_pmt_entry *entry,
|
||||
struct intel_vsec_device *ivdev,
|
||||
int idx)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(ivdev->dev);
|
||||
struct device *dev = &ivdev->auxdev.dev;
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
struct resource *disc_res;
|
||||
u8 bir;
|
||||
|
||||
/*
|
||||
|
|
@ -235,6 +237,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
|||
* For access_type LOCAL, the base address is as follows:
|
||||
* base address = end of discovery region + base offset
|
||||
*/
|
||||
disc_res = &ivdev->resource[idx];
|
||||
entry->base_addr = disc_res->end + 1 + header->base_offset;
|
||||
|
||||
/*
|
||||
|
|
@ -284,6 +287,81 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
|||
}
|
||||
|
||||
entry->pcidev = pci_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmt_resolve_access_acpi(struct intel_pmt_entry *entry,
|
||||
struct intel_vsec_device *ivdev)
|
||||
{
|
||||
struct pci_dev *pci_dev = NULL;
|
||||
struct device *dev = &ivdev->auxdev.dev;
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
u8 bir;
|
||||
|
||||
if (dev_is_pci(ivdev->dev))
|
||||
pci_dev = to_pci_dev(ivdev->dev);
|
||||
|
||||
/*
|
||||
* The base offset should always be 8 byte aligned.
|
||||
*
|
||||
* For non-local access types the lower 3 bits of base offset
|
||||
* contains the index of the base address register where the
|
||||
* telemetry can be found.
|
||||
*/
|
||||
bir = GET_BIR(header->base_offset);
|
||||
|
||||
switch (header->access_type) {
|
||||
case ACCESS_BARID:
|
||||
/* ACPI platform drivers use base_addr */
|
||||
if (ivdev->base_addr) {
|
||||
entry->base_addr = ivdev->base_addr +
|
||||
GET_ADDRESS(header->base_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If base_addr is not provided, then this is an ACPI companion device */
|
||||
if (!pci_dev) {
|
||||
dev_err(dev, "ACCESS_BARID requires PCI BAR resources or base_addr\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry->base_addr = pci_resource_start(pci_dev, bir) +
|
||||
GET_ADDRESS(header->base_offset);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported access type %d for ACPI based PMT\n",
|
||||
header->access_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
||||
struct intel_vsec_device *ivdev,
|
||||
int idx)
|
||||
{
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
struct device *dev = &ivdev->auxdev.dev;
|
||||
int ret;
|
||||
|
||||
switch (ivdev->src) {
|
||||
case INTEL_VSEC_DISC_PCI:
|
||||
ret = pmt_resolve_access_pci(entry, ivdev, idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case INTEL_VSEC_DISC_ACPI:
|
||||
ret = pmt_resolve_access_acpi(entry, ivdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unknown discovery source: %d\n", ivdev->src);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry->guid = header->guid;
|
||||
entry->size = header->size;
|
||||
entry->cb = ivdev->priv_data;
|
||||
|
|
@ -368,24 +446,84 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int pmt_get_headers(struct intel_vsec_device *ivdev, int idx,
|
||||
struct intel_pmt_entry *entry)
|
||||
{
|
||||
struct device *dev = &ivdev->auxdev.dev;
|
||||
size_t header_bytes = sizeof(entry->disc_header);
|
||||
|
||||
switch (ivdev->src) {
|
||||
case INTEL_VSEC_DISC_PCI: {
|
||||
struct resource *disc_res = &ivdev->resource[idx];
|
||||
void __iomem *disc_table;
|
||||
|
||||
disc_table = devm_ioremap_resource(dev, disc_res);
|
||||
if (IS_ERR(disc_table))
|
||||
return PTR_ERR(disc_table);
|
||||
|
||||
/*
|
||||
* The mapped resource is sized by the namespace's DVSEC
|
||||
* entry_size (in dwords), which can be less than the default
|
||||
* size (e.g. telemetry uses entry_size = 3, 12 bytes). Cap the
|
||||
* copy to resource_size() to avoid reading past the mapped
|
||||
* region.
|
||||
*/
|
||||
memset(entry->disc_header, 0, header_bytes);
|
||||
memcpy_fromio(entry->disc_header, disc_table,
|
||||
min(header_bytes, resource_size(disc_res)));
|
||||
|
||||
/* Used by crashlog driver */
|
||||
entry->disc_table = disc_table;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case INTEL_VSEC_DISC_ACPI: {
|
||||
memcpy(entry->disc_header, &ivdev->acpi_disc[idx][0], header_bytes);
|
||||
/*
|
||||
* No MMIO mapping exists on the ACPI source path; the cached
|
||||
* headers are the only view of the discovery record. Consumers
|
||||
* that dereference disc_table (e.g. crashlog) must therefore
|
||||
* only be wired to namespaces backed by INTEL_VSEC_DISC_PCI.
|
||||
*/
|
||||
entry->disc_table = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
dev_err(dev, "Unknown discovery source type: %d\n", ivdev->src);
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns,
|
||||
struct intel_vsec_device *intel_vsec_dev, int idx)
|
||||
{
|
||||
struct device *dev = &intel_vsec_dev->auxdev.dev;
|
||||
struct resource *disc_res;
|
||||
int ret;
|
||||
|
||||
disc_res = &intel_vsec_dev->resource[idx];
|
||||
ret = pmt_get_headers(intel_vsec_dev, idx, entry);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
entry->disc_table = devm_ioremap_resource(dev, disc_res);
|
||||
if (IS_ERR(entry->disc_table))
|
||||
return PTR_ERR(entry->disc_table);
|
||||
if (ns->pmt_pre_decode) {
|
||||
ret = ns->pmt_pre_decode(intel_vsec_dev, entry);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ns->pmt_header_decode(entry, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = intel_pmt_populate_entry(entry, intel_vsec_dev, disc_res);
|
||||
if (ns->pmt_post_decode) {
|
||||
ret = ns->pmt_post_decode(intel_vsec_dev, entry);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = intel_pmt_populate_entry(entry, intel_vsec_dev, idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,12 +37,14 @@ struct intel_pmt_header {
|
|||
u32 size;
|
||||
u32 guid;
|
||||
u8 access_type;
|
||||
u8 telem_type;
|
||||
};
|
||||
|
||||
struct intel_pmt_entry {
|
||||
struct telem_endpoint *ep;
|
||||
struct pci_dev *pcidev;
|
||||
struct intel_pmt_header header;
|
||||
u32 disc_header[PMT_DISC_DWORDS];
|
||||
struct bin_attribute pmt_bin_attr;
|
||||
const struct attribute_group *attr_grp;
|
||||
struct kobject *kobj;
|
||||
|
|
@ -62,6 +64,10 @@ struct intel_pmt_namespace {
|
|||
struct xarray *xa;
|
||||
int (*pmt_header_decode)(struct intel_pmt_entry *entry,
|
||||
struct device *dev);
|
||||
int (*pmt_pre_decode)(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry);
|
||||
int (*pmt_post_decode)(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry);
|
||||
int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -496,11 +496,9 @@ static const struct crashlog_info *select_crashlog_info(u32 type, u32 version)
|
|||
return &crashlog_type1_ver2;
|
||||
}
|
||||
|
||||
static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
||||
struct device *dev)
|
||||
static int pmt_crashlog_pre_decode(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry)
|
||||
{
|
||||
void __iomem *disc_table = entry->disc_table;
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
struct crashlog_entry *crashlog;
|
||||
u32 version;
|
||||
u32 type;
|
||||
|
|
@ -513,6 +511,16 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
|||
mutex_init(&crashlog->control_mutex);
|
||||
|
||||
crashlog->info = select_crashlog_info(type, version);
|
||||
entry->attr_grp = crashlog->info->attr_grp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
||||
struct device *dev)
|
||||
{
|
||||
void __iomem *disc_table = entry->disc_table;
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
|
||||
header->access_type = GET_ACCESS(readl(disc_table));
|
||||
header->guid = readl(disc_table + GUID_OFFSET);
|
||||
|
|
@ -521,8 +529,6 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
|||
/* Size is measured in DWORDS, but accessor returns bytes */
|
||||
header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET));
|
||||
|
||||
entry->attr_grp = crashlog->info->attr_grp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -530,6 +536,7 @@ static DEFINE_XARRAY_ALLOC(crashlog_array);
|
|||
static struct intel_pmt_namespace pmt_crashlog_ns = {
|
||||
.name = "crashlog",
|
||||
.xa = &crashlog_array,
|
||||
.pmt_pre_decode = pmt_crashlog_pre_decode,
|
||||
.pmt_header_decode = pmt_crashlog_header_decode,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -58,14 +58,9 @@ struct pmt_telem_priv {
|
|||
struct intel_pmt_entry entry[];
|
||||
};
|
||||
|
||||
static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
|
||||
struct device *dev)
|
||||
static bool pmt_telem_region_overlaps(struct device *dev, u32 guid, u32 type)
|
||||
{
|
||||
u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
|
||||
|
||||
if (intel_pmt_is_early_client_hw(dev)) {
|
||||
u32 type = TELEM_TYPE(readl(entry->disc_table));
|
||||
|
||||
if ((type == TELEM_TYPE_PUNIT_FIXED) ||
|
||||
(guid == TELEM_CLIENT_FIXED_BLOCK_GUID))
|
||||
return true;
|
||||
|
|
@ -77,18 +72,28 @@ static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
|
|||
static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
|
||||
struct device *dev)
|
||||
{
|
||||
void __iomem *disc_table = entry->disc_table;
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
u32 *disc_header = entry->disc_header;
|
||||
|
||||
if (pmt_telem_region_overlaps(entry, dev))
|
||||
return 1;
|
||||
|
||||
header->access_type = TELEM_ACCESS(readl(disc_table));
|
||||
header->guid = readl(disc_table + TELEM_GUID_OFFSET);
|
||||
header->base_offset = readl(disc_table + TELEM_BASE_OFFSET);
|
||||
header->access_type = TELEM_ACCESS(disc_header[0]);
|
||||
header->guid = disc_header[1];
|
||||
header->base_offset = disc_header[2];
|
||||
|
||||
/* Size is measured in DWORDS, but accessor returns bytes */
|
||||
header->size = TELEM_SIZE(readl(disc_table));
|
||||
header->size = TELEM_SIZE(disc_header[0]);
|
||||
header->telem_type = TELEM_TYPE(disc_header[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmt_telem_post_decode(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry)
|
||||
{
|
||||
struct intel_pmt_header *header = &entry->header;
|
||||
struct device *dev = &ivdev->auxdev.dev;
|
||||
|
||||
if (pmt_telem_region_overlaps(dev, header->guid, header->telem_type))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Some devices may expose non-functioning entries that are
|
||||
|
|
@ -131,6 +136,7 @@ static struct intel_pmt_namespace pmt_telem_ns = {
|
|||
.name = "telem",
|
||||
.xa = &telem_array,
|
||||
.pmt_header_decode = pmt_telem_header_decode,
|
||||
.pmt_post_decode = pmt_telem_post_decode,
|
||||
.pmt_add_endpoint = pmt_telem_add_endpoint,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1804,7 +1804,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)
|
|||
if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_PP_ENABLE))
|
||||
continue;
|
||||
|
||||
writeq(pd_info->saved_pp_control, power_domain_info->sst_base +
|
||||
writeq(pd_info->saved_pp_control, pd_info->sst_base +
|
||||
pd_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
return sysfs_emit(buf, "%u\n", data->domain_id);
|
||||
}
|
||||
|
||||
static ssize_t show_instance_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct uncore_data *data = container_of(attr, struct uncore_data, instance_id_kobj_attr);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", data->instance_id);
|
||||
}
|
||||
|
||||
static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);
|
||||
|
|
@ -200,6 +207,9 @@ static int create_attr_group(struct uncore_data *data, char *name)
|
|||
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
|
||||
init_attribute_root_ro(domain_id);
|
||||
data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr;
|
||||
init_attribute_root_ro(instance_id);
|
||||
data->uncore_attrs[index++] = &data->instance_id_kobj_attr.attr;
|
||||
|
||||
init_attribute_root_ro(fabric_cluster_id);
|
||||
data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr;
|
||||
init_attribute_root_ro(package_id);
|
||||
|
|
@ -268,22 +278,27 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu)
|
|||
if (ret < 0)
|
||||
goto uncore_unlock;
|
||||
|
||||
data->instance_id = ret;
|
||||
data->seqnum_id = ret;
|
||||
scnprintf(data->name, sizeof(data->name), "uncore%02d", ret);
|
||||
} else {
|
||||
scnprintf(data->name, sizeof(data->name), "package_%02d_die_%02d",
|
||||
data->package_id, data->die_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the control CPU before any read path so entry recreation after CPU
|
||||
* hotplug can populate read-only attributes from the new online CPU.
|
||||
*/
|
||||
data->control_cpu = cpu;
|
||||
uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
|
||||
uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ);
|
||||
|
||||
ret = create_attr_group(data, data->name);
|
||||
if (ret) {
|
||||
data->control_cpu = -1;
|
||||
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
|
||||
ida_free(&intel_uncore_ida, data->instance_id);
|
||||
ida_free(&intel_uncore_ida, data->seqnum_id);
|
||||
} else {
|
||||
data->control_cpu = cpu;
|
||||
data->valid = true;
|
||||
}
|
||||
|
||||
|
|
@ -301,7 +316,7 @@ void uncore_freq_remove_die_entry(struct uncore_data *data)
|
|||
data->control_cpu = -1;
|
||||
data->valid = false;
|
||||
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
|
||||
ida_free(&intel_uncore_ida, data->instance_id);
|
||||
ida_free(&intel_uncore_ida, data->seqnum_id);
|
||||
|
||||
mutex_unlock(&uncore_lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@
|
|||
* @die_id: Die id for this instance
|
||||
* @domain_id: Power domain id for this instance
|
||||
* @cluster_id: cluster id in a domain
|
||||
* @instance_id: Unique instance id to append to directory name
|
||||
* @seqnum_id: Unique sequential id to append to directory name
|
||||
* @instance_id: Die indices or feature instances for a single TPMI device
|
||||
* @name: Sysfs entry name for this instance
|
||||
* @agent_type_mask: Bit mask of all hardware agents for this domain
|
||||
* @uncore_attr_group: Attribute group storage
|
||||
|
|
@ -56,6 +57,7 @@
|
|||
* @elc_floor_freq_khz_kobj_attr: Storage for kobject attribute elc_floor_freq_khz
|
||||
* @agent_types_kobj_attr: Storage for kobject attribute agent_type
|
||||
* @die_id_kobj_attr: Attribute storage for die_id information
|
||||
* @instance_id_kobj_attr: Attribute storage for instance_id value
|
||||
* @uncore_attrs: Attribute storage for group creation
|
||||
*
|
||||
* This structure is used to encapsulate all data related to uncore sysfs
|
||||
|
|
@ -71,6 +73,7 @@ struct uncore_data {
|
|||
int die_id;
|
||||
int domain_id;
|
||||
int cluster_id;
|
||||
int seqnum_id;
|
||||
int instance_id;
|
||||
char name[32];
|
||||
u16 agent_type_mask;
|
||||
|
|
@ -90,7 +93,8 @@ struct uncore_data {
|
|||
struct kobj_attribute elc_floor_freq_khz_kobj_attr;
|
||||
struct kobj_attribute agent_types_kobj_attr;
|
||||
struct kobj_attribute die_id_kobj_attr;
|
||||
struct attribute *uncore_attrs[15];
|
||||
struct kobj_attribute instance_id_kobj_attr;
|
||||
struct attribute *uncore_attrs[16];
|
||||
};
|
||||
|
||||
#define UNCORE_DOMAIN_ID_INVALID -1
|
||||
|
|
|
|||
|
|
@ -385,7 +385,19 @@ static u8 io_die_index_next;
|
|||
/* Lock to protect io_die_start, io_die_index_next */
|
||||
static DEFINE_MUTEX(domain_lock);
|
||||
|
||||
static void set_domain_id(int id, int num_resources,
|
||||
static inline void set_instance_id(int id, struct tpmi_uncore_cluster_info *cluster_info)
|
||||
{
|
||||
/*
|
||||
* On non-partitioned systems domain_id can be used for mapping both
|
||||
* CPUs to compute die IDs and physical die indexes to MMIO mapped
|
||||
* memory. However on partitioned systems domain_id loses the second
|
||||
* association. Therefore instance_id should be used for that instead,
|
||||
* while domain_id should still be used to match CPUs to compute dies.
|
||||
*/
|
||||
cluster_info->uncore_data.instance_id = id;
|
||||
}
|
||||
|
||||
static void set_domain_id(int id, int num_resources,
|
||||
struct oobmsm_plat_info *plat_info,
|
||||
struct tpmi_uncore_cluster_info *cluster_info)
|
||||
{
|
||||
|
|
@ -690,6 +702,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
|
|||
set_cdie_id(i, cluster_info, plat_info);
|
||||
|
||||
set_domain_id(i, num_resources, plat_info, cluster_info);
|
||||
set_instance_id(i, cluster_info);
|
||||
|
||||
cluster_info->uncore_root = tpmi_uncore;
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ static void intel_vsec_dev_release(struct device *dev)
|
|||
ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id);
|
||||
|
||||
kfree(intel_vsec_dev->acpi_disc);
|
||||
kfree(intel_vsec_dev->resource);
|
||||
kfree(intel_vsec_dev);
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +224,6 @@ int intel_vsec_add_aux(struct device *parent,
|
|||
ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev,
|
||||
PMT_XA_LIMIT, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
kfree(intel_vsec_dev->resource);
|
||||
kfree(intel_vsec_dev);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -233,7 +231,6 @@ int intel_vsec_add_aux(struct device *parent,
|
|||
id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
xa_erase(&auxdev_array, intel_vsec_dev->id);
|
||||
kfree(intel_vsec_dev->resource);
|
||||
kfree(intel_vsec_dev);
|
||||
return id;
|
||||
}
|
||||
|
|
@ -282,7 +279,7 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
|
|||
unsigned long cap_id, u64 base_addr)
|
||||
{
|
||||
struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL;
|
||||
struct resource __free(kfree) *res = NULL;
|
||||
struct resource *res;
|
||||
struct resource *tmp;
|
||||
struct device *parent;
|
||||
unsigned long quirks = info->quirks;
|
||||
|
|
@ -306,13 +303,12 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
intel_vsec_dev = kzalloc_obj(*intel_vsec_dev);
|
||||
intel_vsec_dev = kzalloc_flex(*intel_vsec_dev, resource, header->num_entries);
|
||||
if (!intel_vsec_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
res = kzalloc_objs(*res, header->num_entries);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
intel_vsec_dev->num_resources = header->num_entries;
|
||||
res = intel_vsec_dev->resource;
|
||||
|
||||
if (quirks & VSEC_QUIRK_TABLE_SHIFT)
|
||||
header->offset >>= TABLE_OFFSET_SHIFT;
|
||||
|
|
@ -342,8 +338,6 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
|
|||
}
|
||||
|
||||
intel_vsec_dev->dev = dev;
|
||||
intel_vsec_dev->resource = no_free_ptr(res);
|
||||
intel_vsec_dev->num_resources = header->num_entries;
|
||||
intel_vsec_dev->quirks = info->quirks;
|
||||
intel_vsec_dev->base_addr = info->base_addr;
|
||||
intel_vsec_dev->priv_data = info->priv_data;
|
||||
|
|
@ -488,10 +482,25 @@ static int intel_vsec_walk_header(struct device *dev,
|
|||
const struct intel_vsec_platform_info *info)
|
||||
{
|
||||
struct intel_vsec_header **header = info->headers;
|
||||
u64 base_addr;
|
||||
int ret;
|
||||
|
||||
for ( ; *header; header++) {
|
||||
ret = intel_vsec_register_device(dev, *header, info, info->base_addr);
|
||||
if (info->base_addr) {
|
||||
base_addr = info->base_addr;
|
||||
} else {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (!dev_is_pci(dev)) {
|
||||
dev_err(dev, "non-PCI device without a base address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
base_addr = pci_resource_start(pdev, (*header)->tbir);
|
||||
}
|
||||
|
||||
ret = intel_vsec_register_device(dev, *header, info, base_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/intel_tpmi.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
|
|
@ -473,7 +474,7 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l
|
|||
struct seq_file *m = file->private_data;
|
||||
struct intel_tpmi_pm_feature *pfs = m->private;
|
||||
u32 addr, value, punit, size;
|
||||
u32 num_elems, *array;
|
||||
u32 num_elems;
|
||||
void __iomem *mem;
|
||||
int ret;
|
||||
|
||||
|
|
@ -481,15 +482,14 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l
|
|||
if (!size)
|
||||
return -EIO;
|
||||
|
||||
u32 *array __free(kfree) = NULL;
|
||||
ret = parse_int_array_user(userbuf, len, (int **)&array);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_elems = *array;
|
||||
if (num_elems != 3) {
|
||||
ret = -EINVAL;
|
||||
goto exit_write;
|
||||
}
|
||||
if (num_elems != 3)
|
||||
return -EINVAL;
|
||||
|
||||
punit = array[1];
|
||||
addr = array[2];
|
||||
|
|
@ -498,37 +498,23 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l
|
|||
if (!IS_ALIGNED(addr, sizeof(u32)))
|
||||
return -EINVAL;
|
||||
|
||||
if (punit >= pfs->pfs_header.num_entries) {
|
||||
ret = -EINVAL;
|
||||
goto exit_write;
|
||||
}
|
||||
if (punit >= pfs->pfs_header.num_entries)
|
||||
return -EINVAL;
|
||||
|
||||
if (addr >= size) {
|
||||
ret = -EINVAL;
|
||||
goto exit_write;
|
||||
}
|
||||
if (addr >= size)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tpmi_dev_lock);
|
||||
guard(mutex)(&tpmi_dev_lock);
|
||||
|
||||
mem = ioremap(pfs->vsec_offset + punit * size, size);
|
||||
if (!mem) {
|
||||
ret = -ENOMEM;
|
||||
goto unlock_mem_write;
|
||||
}
|
||||
if (!mem)
|
||||
return -ENOMEM;
|
||||
|
||||
writel(value, mem + addr);
|
||||
|
||||
iounmap(mem);
|
||||
|
||||
ret = len;
|
||||
|
||||
unlock_mem_write:
|
||||
mutex_unlock(&tpmi_dev_lock);
|
||||
|
||||
exit_write:
|
||||
kfree(array);
|
||||
|
||||
return ret;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int mem_write_show(struct seq_file *s, void *unused)
|
||||
|
|
@ -641,15 +627,12 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
|
|||
if (!name)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = kzalloc_objs(*res, pfs->pfs_header.num_entries);
|
||||
if (!res)
|
||||
feature_vsec_dev = kzalloc_flex(*feature_vsec_dev, resource, pfs->pfs_header.num_entries);
|
||||
if (!feature_vsec_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
feature_vsec_dev = kzalloc_obj(*feature_vsec_dev);
|
||||
if (!feature_vsec_dev) {
|
||||
kfree(res);
|
||||
return -ENOMEM;
|
||||
}
|
||||
feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
|
||||
res = feature_vsec_dev->resource;
|
||||
|
||||
snprintf(feature_id_name, sizeof(feature_id_name), "tpmi-%s", name);
|
||||
|
||||
|
|
@ -662,8 +645,6 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
|
|||
}
|
||||
|
||||
feature_vsec_dev->dev = vsec_dev->dev;
|
||||
feature_vsec_dev->resource = res;
|
||||
feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
|
||||
feature_vsec_dev->priv_data = &tpmi_info->plat_info;
|
||||
feature_vsec_dev->priv_data_size = sizeof(tpmi_info->plat_info);
|
||||
feature_vsec_dev->ida = &intel_vsec_tpmi_ida;
|
||||
|
|
|
|||
|
|
@ -236,6 +236,7 @@ config YT2_1380
|
|||
config LENOVO_WMI_CAPDATA
|
||||
tristate
|
||||
depends on ACPI_WMI
|
||||
depends on LENOVO_WMI_HELPERS
|
||||
|
||||
config LENOVO_WMI_EVENTS
|
||||
tristate
|
||||
|
|
@ -262,6 +263,7 @@ config LENOVO_WMI_GAMEZONE
|
|||
config LENOVO_WMI_TUNING
|
||||
tristate "Lenovo Other Mode WMI Driver"
|
||||
depends on ACPI_WMI
|
||||
depends on ACPI_BATTERY
|
||||
select HWMON
|
||||
select FW_ATTR_CLASS
|
||||
select LENOVO_WMI_CAPDATA
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/backlight.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/freezer.h>
|
||||
|
|
@ -2469,7 +2470,7 @@ static int hotkey_kthread(void *data)
|
|||
bool was_frozen;
|
||||
|
||||
if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
|
||||
goto exit;
|
||||
return 0;
|
||||
|
||||
set_freezable();
|
||||
|
||||
|
|
@ -2526,7 +2527,6 @@ static int hotkey_kthread(void *data)
|
|||
si ^= 1;
|
||||
}
|
||||
|
||||
exit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -11217,6 +11217,26 @@ static ssize_t hwdd_status_show(struct device *dev,
|
|||
|
||||
return sysfs_emit(buf, "0\n");
|
||||
}
|
||||
|
||||
/* sysfs type-c damage detection raw data - accessed via debugfs*/
|
||||
static int hwdd_raw_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned int damage_status;
|
||||
int err;
|
||||
|
||||
if (!ucdd_supported)
|
||||
return -ENODEV;
|
||||
|
||||
/* Get USB TYPE-C damage status */
|
||||
err = hwdd_command(HWDD_GET_DMG_USBC, &damage_status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
seq_printf(m, "HWDD: 0x%x\n", damage_status);
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(hwdd_raw);
|
||||
|
||||
static DEVICE_ATTR_RO(hwdd_status);
|
||||
static DEVICE_ATTR_RO(hwdd_detail);
|
||||
|
||||
|
|
@ -11504,6 +11524,20 @@ static const char * __init str_supported(int is_supported)
|
|||
}
|
||||
#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
|
||||
|
||||
static struct dentry *tpacpi_dbg;
|
||||
static void tpacpi_debugfs_init(void)
|
||||
{
|
||||
tpacpi_dbg = debugfs_create_dir("tpacpi", NULL);
|
||||
|
||||
/* HWDD raw data */
|
||||
debugfs_create_file("hwdd-raw", 0444, tpacpi_dbg, NULL, &hwdd_raw_fops);
|
||||
}
|
||||
|
||||
static void tpacpi_debugfs_remove(void)
|
||||
{
|
||||
debugfs_remove_recursive(tpacpi_dbg);
|
||||
}
|
||||
|
||||
static void ibm_exit(struct ibm_struct *ibm)
|
||||
{
|
||||
dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
|
||||
|
|
@ -12068,6 +12102,8 @@ static void thinkpad_acpi_module_exit(void)
|
|||
remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
|
||||
if (tpacpi_wq)
|
||||
destroy_workqueue(tpacpi_wq);
|
||||
if (tpacpi_dbg)
|
||||
tpacpi_debugfs_remove();
|
||||
|
||||
kfree(thinkpad_id.bios_version_str);
|
||||
kfree(thinkpad_id.ec_version_str);
|
||||
|
|
@ -12198,6 +12234,8 @@ static int __init thinkpad_acpi_module_init(void)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
tpacpi_debugfs_init();
|
||||
|
||||
dmi_id = dmi_first_match(fwbug_list);
|
||||
if (dmi_id)
|
||||
tp_features.quirks = dmi_id->driver_data;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/cleanup.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/err.h>
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
#include <linux/mutex_types.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
|
|
@ -87,6 +89,7 @@ struct lwmi_cd_priv {
|
|||
struct notifier_block acpi_nb; /* ACPI events */
|
||||
struct wmi_device *wdev;
|
||||
struct cd_list *list;
|
||||
struct dentry *debugfs_dir;
|
||||
|
||||
/*
|
||||
* A capdata device may be a component master of another capdata device.
|
||||
|
|
@ -117,6 +120,8 @@ struct cd_list {
|
|||
|
||||
static struct wmi_driver lwmi_cd_driver;
|
||||
|
||||
/* ======== Device components ======== */
|
||||
|
||||
/**
|
||||
* lwmi_cd_match() - Match rule for the master driver.
|
||||
* @dev: Pointer to the capability data parent device.
|
||||
|
|
@ -470,6 +475,116 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
|
|||
DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan);
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA");
|
||||
|
||||
/* ======== debugfs ======== */
|
||||
|
||||
/**
|
||||
* lwmi_cd00_show() - Dump capdata00
|
||||
* @s: Pointer to seq_file where the capdata00 is dumped.
|
||||
* @cd00: Pointer to a capdata00 struct to be dumped.
|
||||
*/
|
||||
static void lwmi_cd00_show(struct seq_file *s, struct capdata00 *cd00)
|
||||
{
|
||||
u8 dev = FIELD_GET(LWMI_ATTR_DEV_ID_MASK, cd00->id);
|
||||
u8 feat = FIELD_GET(LWMI_ATTR_FEAT_ID_MASK, cd00->id);
|
||||
u8 mode = FIELD_GET(LWMI_ATTR_MODE_ID_MASK, cd00->id);
|
||||
u8 type = FIELD_GET(LWMI_ATTR_TYPE_ID_MASK, cd00->id);
|
||||
bool extra = cd00->supported & ~(LWMI_SUPP_GET | LWMI_SUPP_SET | LWMI_SUPP_VALID);
|
||||
bool get = cd00->supported & LWMI_SUPP_GET;
|
||||
bool set = cd00->supported & LWMI_SUPP_SET;
|
||||
bool valid = cd00->supported & LWMI_SUPP_VALID;
|
||||
|
||||
seq_printf(s, " id: 0x%08x [dev: %2u, feat: %2u, mode: %2u, type: %2u]\n",
|
||||
cd00->id, dev, feat, mode, type);
|
||||
|
||||
seq_printf(s, " supported: 0x%08x [%c%c%c%c]\n", cd00->supported,
|
||||
extra ? '+' : ' ',
|
||||
get ? 'R' : ' ',
|
||||
set ? 'W' : ' ',
|
||||
valid ? 'V' : ' ');
|
||||
|
||||
seq_printf(s, " default_value: %u\n", cd00->default_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_cd01_show() - Dump capdata01
|
||||
* @s: Pointer to seq_file where the capdata01 is dumped.
|
||||
* @cd01: Pointer to a capdata01 struct to be dumped.
|
||||
*/
|
||||
static void lwmi_cd01_show(struct seq_file *s, struct capdata01 *cd01)
|
||||
{
|
||||
/* capdata01 is an extension to capdata00. */
|
||||
lwmi_cd00_show(s, &cd01->cd00);
|
||||
|
||||
seq_printf(s, " step: %u\n", cd01->step);
|
||||
seq_printf(s, " min_value: %u\n", cd01->min_value);
|
||||
seq_printf(s, " max_value: %u\n", cd01->max_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_cd_fan_show() - Dump capdata_fan
|
||||
* @s: Pointer to seq_file where the capdata_fan is dumped.
|
||||
* @cd_fan: Pointer to a capdata_fan struct to be dumped.
|
||||
*/
|
||||
static void lwmi_cd_fan_show(struct seq_file *s, struct capdata_fan *cd_fan)
|
||||
{
|
||||
seq_printf(s, " id: %u\n", cd_fan->id);
|
||||
seq_printf(s, " min_rpm: %u\n", cd_fan->min_rpm);
|
||||
seq_printf(s, " max_rpm: %u\n", cd_fan->max_rpm);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_cd_debugfs_show() - Dump capability data to debugfs
|
||||
* @s: Pointer to seq_file where the capability data is dumped.
|
||||
* @data: unused.
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
static int lwmi_cd_debugfs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct lwmi_cd_priv *priv = s->private;
|
||||
u8 idx;
|
||||
|
||||
guard(mutex)(&priv->list->list_mutex);
|
||||
|
||||
/* lwmi_cd_alloc() ensured priv->list->type must be a valid type. */
|
||||
for (idx = 0; idx < priv->list->count; idx++) {
|
||||
seq_printf(s, "%s[%u]:\n", lwmi_cd_table[priv->list->type].name, idx);
|
||||
|
||||
if (priv->list->type == LENOVO_CAPABILITY_DATA_00)
|
||||
lwmi_cd00_show(s, &priv->list->cd00[idx]);
|
||||
else if (priv->list->type == LENOVO_CAPABILITY_DATA_01)
|
||||
lwmi_cd01_show(s, &priv->list->cd01[idx]);
|
||||
else if (priv->list->type == LENOVO_FAN_TEST_DATA)
|
||||
lwmi_cd_fan_show(s, &priv->list->cd_fan[idx]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(lwmi_cd_debugfs);
|
||||
|
||||
/**
|
||||
* lwmi_cd_debugfs_add() - Create debugfs directory and files for a device
|
||||
* @priv: lenovo-wmi-capdata driver data.
|
||||
*/
|
||||
static void lwmi_cd_debugfs_add(struct lwmi_cd_priv *priv)
|
||||
{
|
||||
priv->debugfs_dir = lwmi_debugfs_create_dir(priv->wdev);
|
||||
|
||||
debugfs_create_file("capdata", 0444, priv->debugfs_dir, priv, &lwmi_cd_debugfs_fops);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_cd_debugfs_remove() - Remove debugfs directory for a device
|
||||
* @priv: lenovo-wmi-capdata driver data.
|
||||
*/
|
||||
static void lwmi_cd_debugfs_remove(struct lwmi_cd_priv *priv)
|
||||
{
|
||||
debugfs_remove_recursive(priv->debugfs_dir);
|
||||
priv->debugfs_dir = NULL;
|
||||
}
|
||||
|
||||
/* ======== WMI interface ======== */
|
||||
|
||||
/**
|
||||
* lwmi_cd_cache() - Cache all WMI data block information
|
||||
* @priv: lenovo-wmi-capdata driver data.
|
||||
|
|
@ -772,6 +887,8 @@ static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
|
|||
dev_err(&wdev->dev, "failed to register %s: %d\n",
|
||||
info->name, ret);
|
||||
} else {
|
||||
lwmi_cd_debugfs_add(priv);
|
||||
|
||||
dev_dbg(&wdev->dev, "registered %s with %u items\n",
|
||||
info->name, priv->list->count);
|
||||
}
|
||||
|
|
@ -782,6 +899,8 @@ static void lwmi_cd_remove(struct wmi_device *wdev)
|
|||
{
|
||||
struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
lwmi_cd_debugfs_remove(priv);
|
||||
|
||||
switch (priv->list->type) {
|
||||
case LENOVO_CAPABILITY_DATA_00:
|
||||
lwmi_cd_sub_master_del(priv);
|
||||
|
|
@ -821,6 +940,7 @@ static struct wmi_driver lwmi_cd_driver = {
|
|||
|
||||
module_wmi_driver(lwmi_cd_driver);
|
||||
|
||||
MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
|
||||
MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table);
|
||||
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||
MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@
|
|||
#define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8)
|
||||
#define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0)
|
||||
|
||||
#define LWMI_DEVICE_ID_FAN 0x04
|
||||
enum lwmi_device_id {
|
||||
LWMI_DEVICE_ID_CPU = 0x01,
|
||||
LWMI_DEVICE_ID_GPU = 0x02,
|
||||
LWMI_DEVICE_ID_PSU = 0x03,
|
||||
LWMI_DEVICE_ID_FAN = 0x04,
|
||||
};
|
||||
|
||||
#define LWMI_TYPE_ID_NONE 0x00
|
||||
|
||||
|
|
@ -33,9 +38,10 @@ struct capdata00 {
|
|||
};
|
||||
|
||||
struct capdata01 {
|
||||
u32 id;
|
||||
u32 supported;
|
||||
u32 default_value;
|
||||
union {
|
||||
struct capdata00;
|
||||
struct capdata00 cd00;
|
||||
};
|
||||
u32 step;
|
||||
u32 min_value;
|
||||
u32 max_value;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -185,6 +187,38 @@ int lwmi_tm_notifier_call(enum thermal_mode *mode)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
|
||||
|
||||
static struct dentry *lwmi_debugfs_dir;
|
||||
|
||||
/**
|
||||
* lwmi_debugfs_create_dir() - Helper function for creating a debugfs directory
|
||||
* for a device.
|
||||
* @wdev: Pointer to the WMI device to be called.
|
||||
*
|
||||
* Caller must remove the directory with debugfs_remove_recursive() on device
|
||||
* removal.
|
||||
*
|
||||
* Return: Pointer to the created directory.
|
||||
*/
|
||||
struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev)
|
||||
{
|
||||
return debugfs_create_dir(dev_name(&wdev->dev), lwmi_debugfs_dir);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(lwmi_debugfs_create_dir, "LENOVO_WMI_HELPERS");
|
||||
|
||||
static int __init lwmi_helpers_init(void)
|
||||
{
|
||||
lwmi_debugfs_dir = debugfs_create_dir("lenovo_wmi", NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(lwmi_helpers_init)
|
||||
|
||||
static void __exit lwmi_helpers_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(lwmi_debugfs_dir);
|
||||
}
|
||||
module_exit(lwmi_helpers_exit)
|
||||
|
||||
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||
MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ struct wmi_method_args_32 {
|
|||
u32 arg1;
|
||||
};
|
||||
|
||||
struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev);
|
||||
|
||||
enum lwmi_event_type {
|
||||
LWMI_GZ_GET_THERMAL_MODE = 0x01,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,9 +41,12 @@
|
|||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#include <acpi/battery.h>
|
||||
|
||||
#include "wmi-capdata.h"
|
||||
#include "wmi-events.h"
|
||||
#include "wmi-helpers.h"
|
||||
|
|
@ -51,14 +54,40 @@
|
|||
|
||||
#define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
|
||||
|
||||
#define LWMI_DEVICE_ID_CPU 0x01
|
||||
enum lwmi_feature_id_cpu {
|
||||
LWMI_FEATURE_ID_CPU_SPPT = 0x01,
|
||||
LWMI_FEATURE_ID_CPU_SPL = 0x02,
|
||||
LWMI_FEATURE_ID_CPU_FPPT = 0x03,
|
||||
LWMI_FEATURE_ID_CPU_TEMP = 0x04,
|
||||
LWMI_FEATURE_ID_CPU_APU = 0x05,
|
||||
LWMI_FEATURE_ID_CPU_CL = 0x06,
|
||||
LWMI_FEATURE_ID_CPU_TAU = 0x07,
|
||||
LWMI_FEATURE_ID_CPU_IPL = 0x09,
|
||||
};
|
||||
|
||||
#define LWMI_FEATURE_ID_CPU_SPPT 0x01
|
||||
#define LWMI_FEATURE_ID_CPU_SPL 0x02
|
||||
#define LWMI_FEATURE_ID_CPU_FPPT 0x03
|
||||
enum lwmi_feature_id_gpu {
|
||||
LWMI_FEATURE_ID_GPU_NV_PPAB = 0x01,
|
||||
LWMI_FEATURE_ID_GPU_NV_CTGP = 0x02,
|
||||
LWMI_FEATURE_ID_GPU_TEMP = 0x03,
|
||||
LWMI_FEATURE_ID_GPU_AC_OFFSET = 0x04,
|
||||
LWMI_FEATURE_ID_DGPU_BOOST_CLK = 0x06,
|
||||
LWMI_FEATURE_ID_DGPU_EN = 0x07,
|
||||
LWMI_FEATURE_ID_GPU_MODE = 0x08,
|
||||
LWMI_FEATURE_ID_DGPU_DIDVID = 0x09,
|
||||
LWMI_FEATURE_ID_GPU_NV_BPL = 0x0a,
|
||||
LWMI_FEATURE_ID_GPU_NV_CPU_BOOST = 0x0b,
|
||||
};
|
||||
|
||||
enum lwmi_feature_id_psu {
|
||||
LWMI_FEATURE_ID_PSU_CHARGE_TYPES = 0x01,
|
||||
LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR = 0x02,
|
||||
};
|
||||
|
||||
#define LWMI_FEATURE_ID_FAN_RPM 0x03
|
||||
|
||||
#define LWMI_TYPE_ID_CROSSLOAD 0x01
|
||||
#define LWMI_TYPE_ID_PSU_AC 0x01
|
||||
|
||||
#define LWMI_FEATURE_VALUE_GET 17
|
||||
#define LWMI_FEATURE_VALUE_SET 18
|
||||
|
||||
|
|
@ -68,11 +97,20 @@
|
|||
|
||||
#define LWMI_FAN_DIV 100
|
||||
|
||||
#define LWMI_CHARGE_BEHAVIOR_DISCHARGE 0x00
|
||||
#define LWMI_CHARGE_BEHAVIOR_AUTO 0x01
|
||||
#define LWMI_CHARGE_TYPE_STANDARD 0x00
|
||||
#define LWMI_CHARGE_TYPE_LONGLIFE 0x01
|
||||
|
||||
#define LWMI_ATTR_ID_FAN_RPM(x) \
|
||||
lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \
|
||||
LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x))
|
||||
|
||||
#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
|
||||
#define LWMI_ATTR_ID_PSU(feat, type) \
|
||||
lwmi_attr_id(LWMI_DEVICE_ID_PSU, feat, \
|
||||
LWMI_GZ_THERMAL_MODE_NONE, type)
|
||||
|
||||
#define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
|
||||
#define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
|
||||
|
||||
static DEFINE_IDA(lwmi_om_ida);
|
||||
|
|
@ -111,6 +149,11 @@ struct lwmi_om_priv {
|
|||
bool capdata00_collected : 1;
|
||||
bool capdata_fan_collected : 1;
|
||||
} fan_flags;
|
||||
|
||||
enum power_supply_charge_behaviour charge_behaviour;
|
||||
const struct power_supply_ext *battery_ext;
|
||||
struct acpi_battery_hook battery_hook;
|
||||
bool bh_registered;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -142,6 +185,16 @@ MODULE_PARM_DESC(relax_fan_constraint,
|
|||
"Enabling this may results in HWMON attributes being out-of-sync, "
|
||||
"and setting a too low RPM stops the fan. Use with caution.");
|
||||
|
||||
/* Visibility of power supply extensions */
|
||||
static bool force_load_psy_ext;
|
||||
module_param(force_load_psy_ext, bool, 0444);
|
||||
MODULE_PARM_DESC(force_load_psy_ext,
|
||||
"This option will skip checking if the ideapad_laptop driver will conflict "
|
||||
"with adding an extension to set the battery charge behavior and battery charge "
|
||||
"control end threshold. It will also skip checking if the BIOS reports that "
|
||||
"those features are fully supported. It is recommended to blacklist the ideapad "
|
||||
"driver before using this option.");
|
||||
|
||||
/* ======== HWMON (component: lenovo-wmi-capdata 00 & fan) ======== */
|
||||
|
||||
/**
|
||||
|
|
@ -537,6 +590,380 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
|
|||
lwmi_om_hwmon_add(priv);
|
||||
}
|
||||
|
||||
/* ======== Power Supply Extension (component: lenovo-wmi-capdata 00) ======== */
|
||||
|
||||
/**
|
||||
* lwmi_psy_ext_get_prop() - Get a power_supply_ext property
|
||||
* @ps: The battery that was extended
|
||||
* @ext: The extension
|
||||
* @ext_data: Pointer to the lwmi_om_priv drvdata
|
||||
* @prop: The property to read
|
||||
* @val: The value to return
|
||||
*
|
||||
* Reads the given value from the power_supply_ext property
|
||||
*
|
||||
* Return: 0 on success, or an error
|
||||
*/
|
||||
static int lwmi_psy_ext_get_prop(struct power_supply *ps,
|
||||
const struct power_supply_ext *ext,
|
||||
void *ext_data,
|
||||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct lwmi_om_priv *priv = ext_data;
|
||||
struct wmi_method_args_32 args = {};
|
||||
u32 retval;
|
||||
int ret;
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
||||
/* Reading from BIOS reads the wrong bit. Use cached value */
|
||||
val->intval = priv->charge_behaviour;
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES,
|
||||
LWMI_TYPE_ID_PSU_AC);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
|
||||
(u8 *)&args, sizeof(args),
|
||||
&retval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&priv->wdev->dev, "Got return value %#x for property %#x\n", retval, prop);
|
||||
|
||||
switch (retval) {
|
||||
case LWMI_CHARGE_TYPE_LONGLIFE:
|
||||
val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
|
||||
break;
|
||||
case LWMI_CHARGE_TYPE_STANDARD:
|
||||
val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||
break;
|
||||
default:
|
||||
dev_err(&priv->wdev->dev, "Got invalid charge types value: %#x\n", retval);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_psy_ext_set_prop() - Set a power_supply_ext property
|
||||
* @ps: The battery that was extended
|
||||
* @ext: The extension
|
||||
* @ext_data: Pointer to the lwmi_om_priv drvdata
|
||||
* @prop: The property to write
|
||||
* @val: The value to write
|
||||
*
|
||||
* Writes the given value to the power_supply_ext property
|
||||
*
|
||||
* Return: 0 on success, or an error
|
||||
*/
|
||||
static int lwmi_psy_ext_set_prop(struct power_supply *ps,
|
||||
const struct power_supply_ext *ext,
|
||||
void *ext_data,
|
||||
enum power_supply_property prop,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct lwmi_om_priv *priv = ext_data;
|
||||
struct wmi_method_args_32 args = {};
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
||||
args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR,
|
||||
LWMI_TYPE_ID_NONE);
|
||||
switch (val->intval) {
|
||||
case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
|
||||
args.arg1 = LWMI_CHARGE_BEHAVIOR_AUTO;
|
||||
break;
|
||||
case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
|
||||
args.arg1 = LWMI_CHARGE_BEHAVIOR_DISCHARGE;
|
||||
break;
|
||||
default:
|
||||
dev_err(&priv->wdev->dev, "Got invalid charge behavior value: %#x\n",
|
||||
val->intval);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->charge_behaviour = val->intval;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES,
|
||||
LWMI_TYPE_ID_PSU_AC);
|
||||
switch (val->intval) {
|
||||
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
|
||||
args.arg1 = LWMI_CHARGE_TYPE_LONGLIFE;
|
||||
break;
|
||||
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
|
||||
args.arg1 = LWMI_CHARGE_TYPE_STANDARD;
|
||||
break;
|
||||
default:
|
||||
dev_err(&priv->wdev->dev, "Got invalid charge types value: %#x\n",
|
||||
val->intval);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&priv->wdev->dev, "Attempting to set %#010x for property %#x to %#x\n",
|
||||
args.arg0, prop, args.arg1);
|
||||
|
||||
return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
|
||||
(u8 *)&args, sizeof(args), NULL);
|
||||
}
|
||||
|
||||
/** lwmi_psy_prop_get_supported() - Gets the support level from capdata for a given property
|
||||
* @priv: Pointer to the lwmi_om_priv drvdata
|
||||
* @prop: The power supply property to be evaluated
|
||||
*
|
||||
* Return: bitmapped capability data support level
|
||||
*/
|
||||
static u32 lwmi_psy_prop_get_supported(struct lwmi_om_priv *priv, enum power_supply_property prop)
|
||||
{
|
||||
struct capdata00 capdata;
|
||||
u32 attribute_id;
|
||||
int ret;
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
||||
attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR,
|
||||
LWMI_TYPE_ID_NONE);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES,
|
||||
LWMI_TYPE_ID_PSU_AC);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&priv->wdev->dev, "Battery charge feature (%#010x) support level: %#x\n",
|
||||
attribute_id, capdata.supported);
|
||||
|
||||
return capdata.supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_psy_prop_is_supported() - Determine if the property is supported
|
||||
* @priv: Pointer to the lwmi_om_priv drvdata
|
||||
* @prop: The power supply property to be evaluated
|
||||
*
|
||||
* Checks capdata 00 to determine if the property is supported.
|
||||
*
|
||||
* Return: true if readable, or false
|
||||
*/
|
||||
static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv, enum power_supply_property prop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (force_load_psy_ext)
|
||||
return true;
|
||||
|
||||
ret = lwmi_psy_prop_get_supported(priv, prop);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
return (ret & LWMI_SUPP_VALID) && (ret & LWMI_SUPP_GET);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_psy_prop_is_writeable() - Determine if the property is writeable
|
||||
* @ps: The battery that was extended
|
||||
* @ext: The extension
|
||||
* @ext_data: Pointer the lwmi_om_priv drvdata
|
||||
* @prop: The property to check
|
||||
*
|
||||
* Checks capdata 00 to determine if the property is writable.
|
||||
*
|
||||
* Return: true if writable, or false
|
||||
*/
|
||||
static int lwmi_psy_prop_is_writeable(struct power_supply *ps,
|
||||
const struct power_supply_ext *ext,
|
||||
void *ext_data,
|
||||
enum power_supply_property prop)
|
||||
{
|
||||
struct lwmi_om_priv *priv = ext_data;
|
||||
int ret;
|
||||
|
||||
if (force_load_psy_ext)
|
||||
return true;
|
||||
|
||||
ret = lwmi_psy_prop_get_supported(priv, prop);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
return !!(ret & LWMI_SUPP_SET);
|
||||
}
|
||||
|
||||
#define DEFINE_LWMI_POWER_SUPPLY_EXTENSION(_name, _props, _behaviours, _types) \
|
||||
static const struct power_supply_ext _name = { \
|
||||
.name = LWMI_OM_SYSFS_NAME, \
|
||||
.properties = _props, \
|
||||
.num_properties = ARRAY_SIZE(_props), \
|
||||
.charge_behaviours = _behaviours, \
|
||||
.charge_types = _types, \
|
||||
.get_property = lwmi_psy_ext_get_prop, \
|
||||
.set_property = lwmi_psy_ext_set_prop, \
|
||||
.property_is_writeable = lwmi_psy_prop_is_writeable, \
|
||||
}
|
||||
|
||||
static const enum power_supply_property lwmi_psy_ext_props_all[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPES,
|
||||
};
|
||||
|
||||
static const enum power_supply_property lwmi_psy_ext_props_types[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPES,
|
||||
};
|
||||
|
||||
static const enum power_supply_property lwmi_psy_ext_props_behaviour[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
|
||||
};
|
||||
|
||||
#define LWMI_CHARGE_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \
|
||||
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
|
||||
|
||||
#define LWMI_CHARGE_TYPES (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | \
|
||||
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
|
||||
|
||||
DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_all, lwmi_psy_ext_props_all,
|
||||
LWMI_CHARGE_BEHAVIOURS, LWMI_CHARGE_TYPES);
|
||||
DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_types, lwmi_psy_ext_props_types,
|
||||
0, LWMI_CHARGE_TYPES);
|
||||
DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_behaviour, lwmi_psy_ext_props_behaviour,
|
||||
LWMI_CHARGE_BEHAVIOURS, 0);
|
||||
|
||||
#define LWMI_PSY_PROP_BEHAVIOUR BIT(0)
|
||||
#define LWMI_PSY_PROP_TYPES BIT(1)
|
||||
|
||||
static const struct power_supply_ext *lwmi_psy_exts[] = {
|
||||
[LWMI_PSY_PROP_BEHAVIOUR] = &lwmi_psy_ext_behaviour,
|
||||
[LWMI_PSY_PROP_TYPES] = &lwmi_psy_ext_types,
|
||||
[LWMI_PSY_PROP_BEHAVIOUR | LWMI_PSY_PROP_TYPES] = &lwmi_psy_ext_all,
|
||||
};
|
||||
|
||||
/**
|
||||
* lwmi_add_battery() - Connect the power_supply_ext
|
||||
* @battery: The battery to extend
|
||||
* @hook: The driver hook used to extend the battery
|
||||
*
|
||||
* Return: 0 on success, or an error.
|
||||
*/
|
||||
static int lwmi_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
|
||||
{
|
||||
struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook);
|
||||
|
||||
return power_supply_register_extension(battery, priv->battery_ext, &priv->wdev->dev, priv);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_remove_battery() - Disconnect the power_supply_ext
|
||||
* @battery: The battery that was extended
|
||||
* @hook: The driver hook used to extend the battery
|
||||
*
|
||||
* Return: 0 on success, or an error.
|
||||
*/
|
||||
static int lwmi_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
|
||||
{
|
||||
struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook);
|
||||
|
||||
power_supply_unregister_extension(battery, priv->battery_ext);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_acpi_match() - Attempts to return the ideapad acpi handle
|
||||
* @handle: The ACPI handle that manages battery charging
|
||||
* @lvl: Unused
|
||||
* @context: Void pointer to the acpi_handle object to return
|
||||
* @retval: Unused
|
||||
*
|
||||
* Checks if the ideapad_laptop driver is going to manage charge_type first,
|
||||
* then if not, hooks the battery to our WMI methods.
|
||||
*
|
||||
* Return: AE_CTRL_TERMINATE if found, AE_OK if not found.
|
||||
*/
|
||||
static acpi_status lwmi_acpi_match(acpi_handle handle, u32 lvl,
|
||||
void *context, void **retval)
|
||||
{
|
||||
acpi_handle *ahand = context;
|
||||
|
||||
if (!handle)
|
||||
return AE_OK;
|
||||
|
||||
*ahand = handle;
|
||||
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_om_psy_ext_init() - Hooks power supply extension to device battery
|
||||
* @priv: Pointer to the lwmi_om_priv drvdata.
|
||||
*
|
||||
* Checks if the ideapad_laptop driver is going to manage charge attributes first,
|
||||
* then if not, hooks the battery to our WMI methods if they are supported.
|
||||
*/
|
||||
static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv)
|
||||
{
|
||||
static const char * const ideapad_hid = "VPC2004";
|
||||
acpi_handle handle = NULL;
|
||||
unsigned int props = 0;
|
||||
int ret;
|
||||
|
||||
if (lwmi_psy_prop_is_supported(priv, POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR))
|
||||
props |= LWMI_PSY_PROP_BEHAVIOUR;
|
||||
if (lwmi_psy_prop_is_supported(priv, POWER_SUPPLY_PROP_CHARGE_TYPES))
|
||||
props |= LWMI_PSY_PROP_TYPES;
|
||||
if (!props)
|
||||
return;
|
||||
if (force_load_psy_ext)
|
||||
goto load_psy_ext;
|
||||
|
||||
/* Deconflict ideapad_laptop driver */
|
||||
ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (handle && acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
|
||||
dev_dbg(&priv->wdev->dev, "ideapad_laptop driver manages battery for device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
load_psy_ext:
|
||||
/* Add battery hooks */
|
||||
priv->battery_ext = lwmi_psy_exts[props];
|
||||
priv->battery_hook.add_battery = lwmi_add_battery;
|
||||
priv->battery_hook.remove_battery = lwmi_remove_battery;
|
||||
priv->battery_hook.name = "Lenovo WMI Other Battery Extension";
|
||||
priv->bh_registered = true;
|
||||
|
||||
battery_hook_register(&priv->battery_hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* lwmi_om_psy_remove() - Unregister battery hook
|
||||
* @priv: Driver private data
|
||||
*
|
||||
* Unregisters the battery hook if applicable.
|
||||
*/
|
||||
static void lwmi_om_psy_remove(struct lwmi_om_priv *priv)
|
||||
{
|
||||
if (!priv->bh_registered)
|
||||
return;
|
||||
|
||||
battery_hook_unregister(&priv->battery_hook);
|
||||
priv->bh_registered = false;
|
||||
}
|
||||
|
||||
/* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
|
||||
|
||||
struct tunable_attr_01 {
|
||||
|
|
@ -566,18 +993,132 @@ static struct tunable_attr_01 ppt_pl1_spl = {
|
|||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl1_spl_cl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_SPL,
|
||||
.type_id = LWMI_TYPE_ID_CROSSLOAD,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl2_sppt = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl2_sppt_cl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
|
||||
.type_id = LWMI_TYPE_ID_CROSSLOAD,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl3_fppt = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl3_fppt_cl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
|
||||
.type_id = LWMI_TYPE_ID_CROSSLOAD,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 cpu_temp = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_TEMP,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl1_apu_spl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_APU,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_cpu_cl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_CL,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl1_tau = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_TAU,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl4_ipl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_IPL,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 ppt_pl4_ipl_cl = {
|
||||
.device_id = LWMI_DEVICE_ID_CPU,
|
||||
.feature_id = LWMI_FEATURE_ID_CPU_IPL,
|
||||
.type_id = LWMI_TYPE_ID_CROSSLOAD,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_nv_ppab = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_NV_PPAB,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_nv_ctgp = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_NV_CTGP,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_temp = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_TEMP,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_nv_ac_offset = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_AC_OFFSET,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 dgpu_boost_clk = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_DGPU_BOOST_CLK,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 dgpu_enable = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_DGPU_EN,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_mode = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_MODE,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 dgpu_didvid = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_DGPU_DIDVID,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_nv_bpl = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_NV_BPL,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
static struct tunable_attr_01 gpu_nv_cpu_boost = {
|
||||
.device_id = LWMI_DEVICE_ID_GPU,
|
||||
.feature_id = LWMI_FEATURE_ID_GPU_NV_CPU_BOOST,
|
||||
.type_id = LWMI_TYPE_ID_NONE,
|
||||
};
|
||||
|
||||
struct capdata01_attr_group {
|
||||
const struct attribute_group *attr_group;
|
||||
struct tunable_attr_01 *tunable_attr;
|
||||
|
|
@ -913,17 +1454,77 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
|
|||
.name = _fsname, .attrs = _attrname##_attrs \
|
||||
}
|
||||
|
||||
/* CPU tunable attributes */
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
|
||||
"Set the CPU thermal load limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
|
||||
"Set the CPU cross loading power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_apu_spl, "ppt_pl1_apu_spl",
|
||||
"Set the APU sustained power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl",
|
||||
"Set the CPU sustained power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl_cl, "ppt_pl1_spl_cl",
|
||||
"Set the CPU cross loading sustained power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt",
|
||||
"Set the CPU slow package power tracking limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt_cl, "ppt_pl2_sppt_cl",
|
||||
"Set the CPU cross loading slow package power tracking limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt",
|
||||
"Set the CPU fast package power tracking limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt_cl, "ppt_pl3_fppt_cl",
|
||||
"Set the CPU cross loading fast package power tracking limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_tau, "ppt_pl1_tau",
|
||||
"Set the CPU sustained power limit exceed duration");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
|
||||
"Set the CPU instantaneous power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
|
||||
"Set the CPU cross loading instantaneous power limit");
|
||||
|
||||
/* GPU tunable attributes */
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_boost_clk, "dgpu_boost_clk",
|
||||
"Set the dedicated GPU boost clock");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_didvid, "dgpu_didvid",
|
||||
"Get the GPU device identifier and vendor identifier");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_enable, "dgpu_enable",
|
||||
"Set the dedicated Nvidia GPU enabled status");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_mode, "gpu_mode",
|
||||
"Set the GPU mode by power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ac_offset, "gpu_nv_ac_offset",
|
||||
"Set the Nvidia GPU AC total processing power baseline offset");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_bpl, "gpu_nv_bpl",
|
||||
"Set the Nvidia GPU base power limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_cpu_boost, "gpu_nv_cpu_boost",
|
||||
"Set the Nvidia GPU to CPU dynamic boost limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ctgp, "gpu_nv_ctgp",
|
||||
"Set the GPU configurable total graphics power");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ppab, "gpu_nv_ppab",
|
||||
"Set the Nvidia GPU power performance aware boost limit");
|
||||
LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_temp, "gpu_temp",
|
||||
"Set the GPU thermal load limit");
|
||||
|
||||
static struct capdata01_attr_group cd01_attr_groups[] = {
|
||||
{ &cpu_temp_attr_group, &cpu_temp },
|
||||
{ &dgpu_boost_clk_attr_group, &dgpu_boost_clk },
|
||||
{ &dgpu_didvid_attr_group, &dgpu_didvid },
|
||||
{ &dgpu_enable_attr_group, &dgpu_enable },
|
||||
{ &gpu_mode_attr_group, &gpu_mode },
|
||||
{ &gpu_nv_ac_offset_attr_group, &gpu_nv_ac_offset },
|
||||
{ &gpu_nv_bpl_attr_group, &gpu_nv_bpl },
|
||||
{ &gpu_nv_cpu_boost_attr_group, &gpu_nv_cpu_boost },
|
||||
{ &gpu_nv_ctgp_attr_group, &gpu_nv_ctgp },
|
||||
{ &gpu_nv_ppab_attr_group, &gpu_nv_ppab },
|
||||
{ &gpu_temp_attr_group, &gpu_temp },
|
||||
{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
|
||||
{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
|
||||
{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
|
||||
{ &ppt_pl1_spl_cl_attr_group, &ppt_pl1_spl_cl },
|
||||
{ &ppt_pl1_tau_attr_group, &ppt_pl1_tau },
|
||||
{ &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt },
|
||||
{ &ppt_pl2_sppt_cl_attr_group, &ppt_pl2_sppt_cl },
|
||||
{ &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt },
|
||||
{ &ppt_pl3_fppt_cl_attr_group, &ppt_pl3_fppt_cl },
|
||||
{ &ppt_pl4_ipl_attr_group, &ppt_pl4_ipl },
|
||||
{ &ppt_pl4_ipl_cl_attr_group, &ppt_pl4_ipl_cl },
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
@ -944,8 +1545,7 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
|||
|
||||
priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
|
||||
MKDEV(0, 0), NULL, "%s-%u",
|
||||
LWMI_OM_FW_ATTR_BASE_PATH,
|
||||
priv->ida_id);
|
||||
LWMI_OM_SYSFS_NAME, priv->ida_id);
|
||||
if (IS_ERR(priv->fw_attr_dev)) {
|
||||
err = PTR_ERR(priv->fw_attr_dev);
|
||||
goto err_free_ida;
|
||||
|
|
@ -1050,6 +1650,7 @@ static int lwmi_om_master_bind(struct device *dev)
|
|||
}
|
||||
|
||||
lwmi_om_fan_info_collect_cd00(priv);
|
||||
lwmi_om_psy_ext_init(priv);
|
||||
|
||||
lwmi_om_fw_attr_add(priv);
|
||||
|
||||
|
|
@ -1072,6 +1673,8 @@ static void lwmi_om_master_unbind(struct device *dev)
|
|||
|
||||
lwmi_om_hwmon_remove(priv);
|
||||
|
||||
lwmi_om_psy_remove(priv);
|
||||
|
||||
component_unbind_all(dev, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,16 +20,11 @@
|
|||
#include <linux/input-event-codes.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define TINK_GPIO_DRIVER_NAME "gpio_ich"
|
||||
|
||||
static const struct software_node gpio_ich_node = {
|
||||
.name = TINK_GPIO_DRIVER_NAME,
|
||||
};
|
||||
|
||||
/* LEDs */
|
||||
static const struct software_node tink_gpio_leds_node = {
|
||||
.name = "meraki-mx100-leds",
|
||||
|
|
@ -38,7 +33,7 @@ static const struct software_node tink_gpio_leds_node = {
|
|||
static const struct property_entry tink_internet_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:internet"),
|
||||
PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 11, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -50,7 +45,7 @@ static const struct software_node tink_internet_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan2_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 18, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -62,7 +57,7 @@ static const struct software_node tink_lan2_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan3_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 20, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -74,7 +69,7 @@ static const struct software_node tink_lan3_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan4_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 22, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -86,7 +81,7 @@ static const struct software_node tink_lan4_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan5_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 23, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -98,7 +93,7 @@ static const struct software_node tink_lan5_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan6_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 32, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -110,7 +105,7 @@ static const struct software_node tink_lan6_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan7_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 34, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -122,7 +117,7 @@ static const struct software_node tink_lan7_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan8_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 35, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -134,7 +129,7 @@ static const struct software_node tink_lan8_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan9_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 36, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -146,7 +141,7 @@ static const struct software_node tink_lan9_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan10_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 37, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -158,7 +153,7 @@ static const struct software_node tink_lan10_led_node = {
|
|||
|
||||
static const struct property_entry tink_lan11_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 48, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -170,7 +165,7 @@ static const struct software_node tink_lan11_led_node = {
|
|||
|
||||
static const struct property_entry tink_ha_green_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:ha"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 16, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -182,7 +177,7 @@ static const struct software_node tink_ha_green_led_node = {
|
|||
|
||||
static const struct property_entry tink_ha_orange_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 7, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -194,7 +189,7 @@ static const struct software_node tink_ha_orange_led_node = {
|
|||
|
||||
static const struct property_entry tink_usb_green_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:green:usb"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 21, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -206,7 +201,7 @@ static const struct software_node tink_usb_green_led_node = {
|
|||
|
||||
static const struct property_entry tink_usb_orange_led_props[] = {
|
||||
PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 19, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -230,7 +225,7 @@ static const struct software_node tink_gpio_keys_node = {
|
|||
static const struct property_entry tink_reset_key_props[] = {
|
||||
PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
|
||||
PROPERTY_ENTRY_STRING("label", "Reset"),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 60, GPIO_ACTIVE_LOW),
|
||||
PROPERTY_ENTRY_U32("linux,input-type", EV_KEY),
|
||||
PROPERTY_ENTRY_U32("debounce-interval", 100),
|
||||
{ }
|
||||
|
|
@ -243,7 +238,6 @@ static const struct software_node tink_reset_key_node = {
|
|||
};
|
||||
|
||||
static const struct software_node *tink_swnodes[] = {
|
||||
&gpio_ich_node,
|
||||
/* LEDs nodes */
|
||||
&tink_gpio_leds_node,
|
||||
&tink_internet_led_node,
|
||||
|
|
@ -348,3 +342,4 @@ MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");
|
|||
MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:meraki-mx100");
|
||||
MODULE_IMPORT_NS("LPC_ICH");
|
||||
|
|
|
|||
|
|
@ -823,6 +823,7 @@ static struct msi_ec_conf CONF9 __initdata = {
|
|||
|
||||
static const char * const ALLOWED_FW_10[] __initconst = {
|
||||
"1582EMS1.107", // GF66 11UC
|
||||
"1583EMS1.109", // Pulse GL66 12UEK
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -205,6 +205,13 @@ static const struct dmi_system_id dmi_table[] = {
|
|||
},
|
||||
.driver_data = (void *)oxp_g1_a,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER SUPER X"),
|
||||
},
|
||||
.driver_data = (void *)oxp_g1_a,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
||||
|
|
|
|||
|
|
@ -246,11 +246,11 @@ struct pcc_acpi {
|
|||
int ac_brightness;
|
||||
int dc_brightness;
|
||||
int current_brightness;
|
||||
u32 *sinf;
|
||||
struct acpi_device *device;
|
||||
struct input_dev *input_dev;
|
||||
struct backlight_device *backlight;
|
||||
struct platform_device *platform;
|
||||
u32 sinf[] __counted_by(num_sifr);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -1007,21 +1007,15 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev)
|
|||
*/
|
||||
num_sifr++;
|
||||
|
||||
pcc = kzalloc_obj(struct pcc_acpi);
|
||||
pcc = kzalloc_flex(*pcc, sinf, num_sifr);
|
||||
if (!pcc) {
|
||||
pr_err("Couldn't allocate mem for pcc");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL);
|
||||
if (!pcc->sinf) {
|
||||
result = -ENOMEM;
|
||||
goto out_hotkey;
|
||||
}
|
||||
|
||||
pcc->num_sifr = num_sifr;
|
||||
pcc->device = device;
|
||||
pcc->handle = device->handle;
|
||||
pcc->num_sifr = num_sifr;
|
||||
device->driver_data = pcc;
|
||||
strscpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
|
||||
strscpy(acpi_device_class(device), ACPI_PCC_CLASS);
|
||||
|
|
@ -1029,7 +1023,7 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev)
|
|||
result = acpi_pcc_init_input(pcc);
|
||||
if (result) {
|
||||
pr_err("Error installing keyinput handler\n");
|
||||
goto out_sinf;
|
||||
goto out_hotkey;
|
||||
}
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc)) {
|
||||
|
|
@ -1110,10 +1104,8 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev)
|
|||
backlight_device_unregister(pcc->backlight);
|
||||
out_input:
|
||||
input_unregister_device(pcc->input_dev);
|
||||
out_sinf:
|
||||
device->driver_data = NULL;
|
||||
kfree(pcc->sinf);
|
||||
out_hotkey:
|
||||
device->driver_data = NULL;
|
||||
kfree(pcc);
|
||||
|
||||
return result;
|
||||
|
|
@ -1143,7 +1135,6 @@ static void acpi_pcc_hotkey_remove(struct platform_device *pdev)
|
|||
|
||||
device->driver_data = NULL;
|
||||
|
||||
kfree(pcc->sinf);
|
||||
kfree(pcc);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ static struct platform_device * __init apu_create_pdev(const char *name,
|
|||
.id = PLATFORM_DEVID_NONE,
|
||||
.data = data,
|
||||
.size_data = size,
|
||||
.fwnode = software_node_fwnode(swnode),
|
||||
.swnode = swnode,
|
||||
};
|
||||
struct platform_device *pdev;
|
||||
int err;
|
||||
|
|
|
|||
|
|
@ -570,8 +570,8 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
/* Data for PCI driver interface used by pci_match_id() call below */
|
||||
static const struct pci_device_id pmc_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_data },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_data },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), .driver_data = (kernel_ulong_t)&byt_data },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), .driver_data = (kernel_ulong_t)&cht_data },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/leds.h>
|
||||
|
|
@ -30,19 +32,82 @@
|
|||
#define SEL_PS_B_DETECT "sel_ps_b_detect"
|
||||
#define SEL_PS_B_GOOD "sel_ps_b_good"
|
||||
|
||||
#define AUX_LED_GRN1 "sel_aux_led_grn1"
|
||||
#define AUX_LED_GRN2 "sel_aux_led_grn2"
|
||||
#define AUX_LED_GRN3 "sel_aux_led_grn3"
|
||||
#define AUX_LED_GRN4 "sel_aux_led_grn4"
|
||||
#define ALARM_STATE_USER "sel_alarm_state_user"
|
||||
#define ENABLE_STATE_USER "sel_enable_state_user"
|
||||
#define AUX_LED_RED1 "sel_aux_led_red1"
|
||||
#define AUX_LED_RED2 "sel_aux_led_red2"
|
||||
#define AUX_LED_RED3 "sel_aux_led_red3"
|
||||
#define AUX_LED_RED4 "sel_aux_led_red4"
|
||||
|
||||
static const char *const sel3350_leds_gpio_names[] = {
|
||||
AUX_LED_GRN1,
|
||||
AUX_LED_GRN2,
|
||||
AUX_LED_GRN3,
|
||||
AUX_LED_GRN4,
|
||||
ALARM_STATE_USER,
|
||||
ENABLE_STATE_USER,
|
||||
AUX_LED_RED1,
|
||||
AUX_LED_RED2,
|
||||
AUX_LED_RED3,
|
||||
AUX_LED_RED4,
|
||||
};
|
||||
|
||||
/* LEDs */
|
||||
static const struct gpio_led sel3350_leds[] = {
|
||||
{ .name = "sel:green:aux1" },
|
||||
{ .name = "sel:green:aux2" },
|
||||
{ .name = "sel:green:aux3" },
|
||||
{ .name = "sel:green:aux4" },
|
||||
{ .name = "sel:red:alarm" },
|
||||
static struct gpio_led sel3350_leds[] = {
|
||||
{ .name = "sel:green:aux1",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:green:aux2",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:green:aux3",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:green:aux4",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:red:alarm",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:green:enabled",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_ON },
|
||||
{ .name = "sel:red:aux1" },
|
||||
{ .name = "sel:red:aux2" },
|
||||
{ .name = "sel:red:aux3" },
|
||||
{ .name = "sel:red:aux4" },
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:red:aux1",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:red:aux2",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:red:aux3",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
{ .name = "sel:red:aux4",
|
||||
.default_state = LEDS_GPIO_DEFSTATE_KEEP,
|
||||
.retain_state_suspended = 1,
|
||||
.retain_state_shutdown = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct gpio_led_platform_data sel3350_leds_pdata = {
|
||||
|
|
@ -50,25 +115,6 @@ static const struct gpio_led_platform_data sel3350_leds_pdata = {
|
|||
.leds = sel3350_leds,
|
||||
};
|
||||
|
||||
/* Map GPIOs to LEDs */
|
||||
static struct gpiod_lookup_table sel3350_leds_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
GPIO_LOOKUP_IDX(BXT_NW, 49, NULL, 0, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_NW, 50, NULL, 1, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_NW, 51, NULL, 2, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_NW, 52, NULL, 3, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_W, 20, NULL, 4, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_W, 21, NULL, 5, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_SW, 37, NULL, 6, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_SW, 38, NULL, 7, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_SW, 39, NULL, 8, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP_IDX(BXT_SW, 40, NULL, 9, GPIO_ACTIVE_HIGH),
|
||||
{},
|
||||
}
|
||||
};
|
||||
|
||||
/* Map GPIOs to power supplies */
|
||||
static struct gpiod_lookup_table sel3350_gpios_table = {
|
||||
.dev_id = B2093_GPIO_ACPI_ID ":00",
|
||||
.table = {
|
||||
|
|
@ -76,6 +122,16 @@ static struct gpiod_lookup_table sel3350_gpios_table = {
|
|||
GPIO_LOOKUP(BXT_NW, 45, SEL_PS_A_GOOD, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP(BXT_NW, 46, SEL_PS_B_DETECT, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP(BXT_NW, 47, SEL_PS_B_GOOD, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP(BXT_NW, 49, AUX_LED_GRN1, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_NW, 50, AUX_LED_GRN2, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_NW, 51, AUX_LED_GRN3, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_NW, 52, AUX_LED_GRN4, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_W, 20, ALARM_STATE_USER, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_W, 21, ENABLE_STATE_USER, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_SW, 37, AUX_LED_RED1, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_SW, 38, AUX_LED_RED2, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_SW, 39, AUX_LED_RED3, GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP(BXT_SW, 40, AUX_LED_RED4, GPIO_ACTIVE_HIGH),
|
||||
{},
|
||||
}
|
||||
};
|
||||
|
|
@ -149,6 +205,7 @@ struct sel3350_data {
|
|||
static int sel3350_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rs;
|
||||
int i;
|
||||
struct sel3350_data *sel3350;
|
||||
struct power_supply_config ps_cfg = {};
|
||||
|
||||
|
|
@ -158,9 +215,19 @@ static int sel3350_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, sel3350);
|
||||
|
||||
gpiod_add_lookup_table(&sel3350_leds_table);
|
||||
gpiod_add_lookup_table(&sel3350_gpios_table);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sel3350_leds); ++i) {
|
||||
sel3350_leds[i].gpiod = devm_gpiod_get(&pdev->dev,
|
||||
sel3350_leds_gpio_names[i],
|
||||
GPIOD_ASIS);
|
||||
if (IS_ERR_OR_NULL(sel3350_leds[i].gpiod)) {
|
||||
rs = -EPROBE_DEFER;
|
||||
goto err_gpio_loop;
|
||||
}
|
||||
gpiod_set_consumer_name(sel3350_leds[i].gpiod, sel3350_leds[i].name);
|
||||
}
|
||||
|
||||
sel3350->leds_pdev = platform_device_register_data(
|
||||
NULL,
|
||||
"leds-gpio",
|
||||
|
|
@ -209,11 +276,15 @@ static int sel3350_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
err_gpio_loop:
|
||||
while (i--)
|
||||
devm_gpiod_put(&pdev->dev, sel3350_leds[i].gpiod);
|
||||
goto err_platform;
|
||||
|
||||
err_ps:
|
||||
platform_device_unregister(sel3350->leds_pdev);
|
||||
err_platform:
|
||||
gpiod_remove_lookup_table(&sel3350_gpios_table);
|
||||
gpiod_remove_lookup_table(&sel3350_leds_table);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
|
@ -224,7 +295,6 @@ static void sel3350_remove(struct platform_device *pdev)
|
|||
|
||||
platform_device_unregister(sel3350->leds_pdev);
|
||||
gpiod_remove_lookup_table(&sel3350_gpios_table);
|
||||
gpiod_remove_lookup_table(&sel3350_leds_table);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id sel3350_device_ids[] = {
|
||||
|
|
|
|||
|
|
@ -254,6 +254,10 @@
|
|||
|
||||
#define EC_ADDR_OEM_4 0x07A6
|
||||
#define OVERBOOST_DYN_TEMP_OFF BIT(1)
|
||||
#define CHARGING_PROFILE_MASK GENMASK(5, 4)
|
||||
#define CHARGING_PROFILE_HIGH_CAPACITY 0x00
|
||||
#define CHARGING_PROFILE_BALANCED 0x01
|
||||
#define CHARGING_PROFILE_STATIONARY 0x02
|
||||
#define TOUCHPAD_TOGGLE_OFF BIT(6)
|
||||
|
||||
#define EC_ADDR_CHARGE_CTRL 0x07B9
|
||||
|
|
@ -320,13 +324,15 @@
|
|||
#define UNIWILL_FEATURE_SUPER_KEY BIT(1)
|
||||
#define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2)
|
||||
#define UNIWILL_FEATURE_LIGHTBAR BIT(3)
|
||||
#define UNIWILL_FEATURE_BATTERY BIT(4)
|
||||
#define UNIWILL_FEATURE_CPU_TEMP BIT(5)
|
||||
#define UNIWILL_FEATURE_GPU_TEMP BIT(6)
|
||||
#define UNIWILL_FEATURE_PRIMARY_FAN BIT(7)
|
||||
#define UNIWILL_FEATURE_SECONDARY_FAN BIT(8)
|
||||
#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(9)
|
||||
#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(10)
|
||||
#define UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT BIT(4)
|
||||
/* Mutually exclusive with the charge limit feature */
|
||||
#define UNIWILL_FEATURE_BATTERY_CHARGE_MODES BIT(5)
|
||||
#define UNIWILL_FEATURE_CPU_TEMP BIT(6)
|
||||
#define UNIWILL_FEATURE_GPU_TEMP BIT(7)
|
||||
#define UNIWILL_FEATURE_PRIMARY_FAN BIT(8)
|
||||
#define UNIWILL_FEATURE_SECONDARY_FAN BIT(9)
|
||||
#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(10)
|
||||
#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(11)
|
||||
|
||||
enum usb_c_power_priority_options {
|
||||
USB_C_POWER_PRIORITY_CHARGING = 0,
|
||||
|
|
@ -339,10 +345,18 @@ struct uniwill_data {
|
|||
struct regmap *regmap;
|
||||
unsigned int features;
|
||||
struct acpi_battery_hook hook;
|
||||
unsigned int last_charge_ctrl;
|
||||
struct mutex battery_lock; /* Protects the list of currently registered batteries */
|
||||
unsigned int last_status;
|
||||
unsigned int last_switch_status;
|
||||
union {
|
||||
struct {
|
||||
/* Protects writes to last_charge_type */
|
||||
struct mutex charge_type_lock;
|
||||
enum power_supply_charge_type last_charge_type;
|
||||
};
|
||||
unsigned int last_charge_ctrl;
|
||||
};
|
||||
bool last_fn_lock_state;
|
||||
bool last_super_key_enable_state;
|
||||
bool last_touchpad_toggle_enable_state;
|
||||
struct mutex super_key_lock; /* Protects the toggling of the super key lock state */
|
||||
struct list_head batteries;
|
||||
struct mutex led_lock; /* Protects writes to the lightbar registers */
|
||||
|
|
@ -446,6 +460,12 @@ static inline bool uniwill_device_supports(const struct uniwill_data *data,
|
|||
return (data->features & features) == features;
|
||||
}
|
||||
|
||||
static inline bool uniwill_device_supports_any(const struct uniwill_data *data,
|
||||
unsigned int features)
|
||||
{
|
||||
return data->features & features;
|
||||
}
|
||||
|
||||
static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
union acpi_object params[2] = {
|
||||
|
|
@ -598,6 +618,7 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg)
|
|||
case EC_ADDR_PWM_2:
|
||||
case EC_ADDR_TRIGGER:
|
||||
case EC_ADDR_SWITCH_STATUS:
|
||||
case EC_ADDR_OEM_4:
|
||||
case EC_ADDR_CHARGE_CTRL:
|
||||
case EC_ADDR_USB_C_POWER_PRIORITY:
|
||||
return true;
|
||||
|
|
@ -619,11 +640,22 @@ static const struct regmap_config uniwill_ec_config = {
|
|||
.use_single_write = true,
|
||||
};
|
||||
|
||||
static int uniwill_write_fn_lock(struct uniwill_data *data, bool status)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
if (status)
|
||||
value = FN_LOCK_STATUS;
|
||||
else
|
||||
value = 0;
|
||||
|
||||
return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value);
|
||||
}
|
||||
|
||||
static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
unsigned int value;
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
|
|
@ -631,21 +663,15 @@ static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
value = FN_LOCK_STATUS;
|
||||
else
|
||||
value = 0;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value);
|
||||
ret = uniwill_write_fn_lock(data, enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static int uniwill_read_fn_lock(struct uniwill_data *data, bool *status)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
|
|
@ -653,23 +679,31 @@ static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, c
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !!(value & FN_LOCK_STATUS));
|
||||
*status = !!(value & FN_LOCK_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
bool status;
|
||||
int ret;
|
||||
|
||||
ret = uniwill_read_fn_lock(data, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", status);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(fn_lock);
|
||||
|
||||
static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static int uniwill_write_super_key_enable(struct uniwill_data *data, bool status)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
unsigned int value;
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
guard(mutex)(&data->super_key_lock);
|
||||
|
||||
ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
|
||||
|
|
@ -680,37 +714,17 @@ static ssize_t super_key_enable_store(struct device *dev, struct device_attribut
|
|||
* We can only toggle the super key lock, so we return early if the setting
|
||||
* is already in the correct state.
|
||||
*/
|
||||
if (enable == !(value & SUPER_KEY_LOCK_STATUS))
|
||||
return count;
|
||||
if (status == !(value & SUPER_KEY_LOCK_STATUS))
|
||||
return 0;
|
||||
|
||||
ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK,
|
||||
TRIGGER_SUPER_KEY_LOCK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK,
|
||||
TRIGGER_SUPER_KEY_LOCK);
|
||||
}
|
||||
|
||||
static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !(value & SUPER_KEY_LOCK_STATUS));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(super_key_enable);
|
||||
|
||||
static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
unsigned int value;
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
|
|
@ -718,22 +732,74 @@ static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_at
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
value = 0;
|
||||
else
|
||||
value = TOUCHPAD_TOGGLE_OFF;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value);
|
||||
ret = uniwill_write_super_key_enable(data, enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int uniwill_read_super_key_enable(struct uniwill_data *data, bool *status)
|
||||
{
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*status = !(value & SUPER_KEY_LOCK_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
bool status;
|
||||
int ret;
|
||||
|
||||
ret = uniwill_read_super_key_enable(data, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", status);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(super_key_enable);
|
||||
|
||||
static int uniwill_write_touchpad_toggle_enable(struct uniwill_data *data, bool status)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
if (status)
|
||||
value = 0;
|
||||
else
|
||||
value = TOUCHPAD_TOGGLE_OFF;
|
||||
|
||||
return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value);
|
||||
}
|
||||
|
||||
static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = uniwill_write_touchpad_toggle_enable(data, enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int uniwill_read_touchpad_toggle_enable(struct uniwill_data *data, bool *status)
|
||||
{
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
|
|
@ -741,7 +807,23 @@ static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_att
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !(value & TOUCHPAD_TOGGLE_OFF));
|
||||
*status = !(value & TOUCHPAD_TOGGLE_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct uniwill_data *data = dev_get_drvdata(dev);
|
||||
bool status;
|
||||
int ret;
|
||||
|
||||
ret = uniwill_read_touchpad_toggle_enable(data, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", status);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(touchpad_toggle_enable);
|
||||
|
|
@ -1369,6 +1451,30 @@ static unsigned int uniwill_sanitize_battery_threshold(unsigned int value)
|
|||
return min(value, 100);
|
||||
}
|
||||
|
||||
static int uniwill_read_charge_type(struct uniwill_data *data, enum power_supply_charge_type *type)
|
||||
{
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (FIELD_GET(CHARGING_PROFILE_MASK, value)) {
|
||||
case CHARGING_PROFILE_HIGH_CAPACITY:
|
||||
*type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||
return 0;
|
||||
case CHARGING_PROFILE_BALANCED:
|
||||
*type = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
|
||||
return 0;
|
||||
case CHARGING_PROFILE_STATIONARY:
|
||||
*type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
||||
return 0;
|
||||
default:
|
||||
return -EPROTO;
|
||||
}
|
||||
}
|
||||
|
||||
static int uniwill_get_property(struct power_supply *psy, const struct power_supply_ext *ext,
|
||||
void *drvdata, enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
|
|
@ -1379,6 +1485,16 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup
|
|||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
/*
|
||||
* We need to use the cached value here because the charging mode
|
||||
* reported by the EC might temporarily change when a external power
|
||||
* source has been connected.
|
||||
*/
|
||||
mutex_lock(&data->charge_type_lock);
|
||||
val->intval = data->last_charge_type;
|
||||
mutex_unlock(&data->charge_type_lock);
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_PRESENT, &prop);
|
||||
if (ret < 0)
|
||||
|
|
@ -1423,13 +1539,52 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup
|
|||
}
|
||||
}
|
||||
|
||||
static int uniwill_write_charge_type(struct uniwill_data *data, enum power_supply_charge_type type)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
switch (type) {
|
||||
case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
|
||||
value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_STATIONARY);
|
||||
break;
|
||||
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
|
||||
value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_HIGH_CAPACITY);
|
||||
break;
|
||||
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
|
||||
value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_BALANCED);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, CHARGING_PROFILE_MASK, value);
|
||||
}
|
||||
|
||||
static int uniwill_restore_charge_type(struct uniwill_data *data)
|
||||
{
|
||||
guard(mutex)(&data->charge_type_lock);
|
||||
|
||||
return uniwill_write_charge_type(data, data->last_charge_type);
|
||||
}
|
||||
|
||||
static int uniwill_set_property(struct power_supply *psy, const struct power_supply_ext *ext,
|
||||
void *drvdata, enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct uniwill_data *data = drvdata;
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
mutex_lock(&data->charge_type_lock);
|
||||
|
||||
ret = uniwill_write_charge_type(data, val->intval);
|
||||
if (ret >= 0)
|
||||
data->last_charge_type = val->intval;
|
||||
|
||||
mutex_unlock(&data->charge_type_lock);
|
||||
|
||||
return ret;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
if (val->intval < 0 || val->intval > 100)
|
||||
return -EINVAL;
|
||||
|
|
@ -1445,21 +1600,41 @@ static int uniwill_property_is_writeable(struct power_supply *psy,
|
|||
const struct power_supply_ext *ext, void *drvdata,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
return true;
|
||||
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const enum power_supply_property uniwill_properties[] = {
|
||||
static const enum power_supply_property uniwill_charge_limit_properties[] = {
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
|
||||
};
|
||||
|
||||
static const struct power_supply_ext uniwill_extension = {
|
||||
static const struct power_supply_ext uniwill_charge_limit_extension = {
|
||||
.name = DRIVER_NAME,
|
||||
.properties = uniwill_properties,
|
||||
.num_properties = ARRAY_SIZE(uniwill_properties),
|
||||
.properties = uniwill_charge_limit_properties,
|
||||
.num_properties = ARRAY_SIZE(uniwill_charge_limit_properties),
|
||||
.get_property = uniwill_get_property,
|
||||
.set_property = uniwill_set_property,
|
||||
.property_is_writeable = uniwill_property_is_writeable,
|
||||
};
|
||||
|
||||
static const enum power_supply_property uniwill_charge_modes_properties[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPES,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
};
|
||||
|
||||
static const struct power_supply_ext uniwill_charge_modes_extension = {
|
||||
.name = DRIVER_NAME,
|
||||
.charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) |
|
||||
BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
|
||||
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE),
|
||||
.properties = uniwill_charge_modes_properties,
|
||||
.num_properties = ARRAY_SIZE(uniwill_charge_modes_properties),
|
||||
.get_property = uniwill_get_property,
|
||||
.set_property = uniwill_set_property,
|
||||
.property_is_writeable = uniwill_property_is_writeable,
|
||||
|
|
@ -1475,7 +1650,13 @@ static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery
|
|||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = power_supply_register_extension(battery, &uniwill_extension, data->dev, data);
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT))
|
||||
ret = power_supply_register_extension(battery, &uniwill_charge_limit_extension,
|
||||
data->dev, data);
|
||||
else
|
||||
ret = power_supply_register_extension(battery, &uniwill_charge_modes_extension,
|
||||
data->dev, data);
|
||||
|
||||
if (ret < 0) {
|
||||
kfree(entry);
|
||||
return ret;
|
||||
|
|
@ -1504,7 +1685,10 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt
|
|||
}
|
||||
}
|
||||
|
||||
power_supply_unregister_extension(battery, &uniwill_extension);
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT))
|
||||
power_supply_unregister_extension(battery, &uniwill_charge_limit_extension);
|
||||
else
|
||||
power_supply_unregister_extension(battery, &uniwill_charge_modes_extension);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1514,28 +1698,37 @@ static int uniwill_battery_init(struct uniwill_data *data)
|
|||
unsigned int value, threshold, sanitized;
|
||||
int ret;
|
||||
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The charge control threshold might be initialized with 0 by
|
||||
* the EC to signal that said threshold is uninitialized. We thus
|
||||
* need to replace this placeholder value with a valid one (100)
|
||||
* to signal that we want to take control of battery charging.
|
||||
* For the sake of completeness we also apply this to other
|
||||
* invalid threshold values.
|
||||
*/
|
||||
threshold = FIELD_GET(CHARGE_CTRL_MASK, value);
|
||||
sanitized = uniwill_sanitize_battery_threshold(threshold);
|
||||
if (threshold != sanitized) {
|
||||
FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized);
|
||||
ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value);
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) {
|
||||
ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The charge control threshold might be initialized with 0 by
|
||||
* the EC to signal that said threshold is uninitialized. We thus
|
||||
* need to replace this placeholder value with a valid one (100)
|
||||
* to signal that we want to take control of battery charging.
|
||||
* For the sake of completeness we also apply this to other
|
||||
* invalid threshold values.
|
||||
*/
|
||||
threshold = FIELD_GET(CHARGE_CTRL_MASK, value);
|
||||
sanitized = uniwill_sanitize_battery_threshold(threshold);
|
||||
if (threshold != sanitized) {
|
||||
FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized);
|
||||
ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
} else if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) {
|
||||
ret = devm_mutex_init(data->dev, &data->charge_type_lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = uniwill_read_charge_type(data, &data->last_charge_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = devm_mutex_init(data->dev, &data->battery_lock);
|
||||
|
|
@ -1554,10 +1747,13 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action
|
|||
{
|
||||
struct uniwill_data *data = container_of(nb, struct uniwill_data, nb);
|
||||
struct uniwill_battery_entry *entry;
|
||||
int ret;
|
||||
|
||||
switch (action) {
|
||||
case UNIWILL_OSD_BATTERY_ALERT:
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
|
||||
if (!uniwill_device_supports_any(data,
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
mutex_lock(&data->battery_lock);
|
||||
|
|
@ -1568,10 +1764,24 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action
|
|||
|
||||
return NOTIFY_OK;
|
||||
case UNIWILL_OSD_DC_ADAPTER_CHANGED:
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY))
|
||||
if (!uniwill_device_supports_any(data,
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_USB_C_POWER_PRIORITY))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
return notifier_from_errno(usb_c_power_priority_restore(data));
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) {
|
||||
ret = uniwill_restore_charge_type(data);
|
||||
if (ret < 0)
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) {
|
||||
ret = usb_c_power_priority_restore(data);
|
||||
if (ret < 0)
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
case UNIWILL_OSD_FN_LOCK:
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK))
|
||||
return NOTIFY_DONE;
|
||||
|
|
@ -1723,10 +1933,10 @@ static int uniwill_suspend_fn_lock(struct uniwill_data *data)
|
|||
return 0;
|
||||
|
||||
/*
|
||||
* The EC_ADDR_BIOS_OEM is marked as volatile, so we have to restore it
|
||||
* EC_ADDR_BIOS_OEM is marked as volatile, so we have to restore it
|
||||
* ourselves.
|
||||
*/
|
||||
return regmap_read(data->regmap, EC_ADDR_BIOS_OEM, &data->last_status);
|
||||
return uniwill_read_fn_lock(data, &data->last_fn_lock_state);
|
||||
}
|
||||
|
||||
static int uniwill_suspend_super_key(struct uniwill_data *data)
|
||||
|
|
@ -1735,15 +1945,27 @@ static int uniwill_suspend_super_key(struct uniwill_data *data)
|
|||
return 0;
|
||||
|
||||
/*
|
||||
* The EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it
|
||||
* EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it
|
||||
* ourselves.
|
||||
*/
|
||||
return regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &data->last_switch_status);
|
||||
return uniwill_read_super_key_enable(data, &data->last_super_key_enable_state);
|
||||
}
|
||||
|
||||
static int uniwill_suspend_touchpad_toggle(struct uniwill_data *data)
|
||||
{
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* EC_ADDR_OEM_4 is marked as volatile, so we have to restore it
|
||||
* ourselves.
|
||||
*/
|
||||
return uniwill_read_touchpad_toggle_enable(data, &data->last_touchpad_toggle_enable_state);
|
||||
}
|
||||
|
||||
static int uniwill_suspend_battery(struct uniwill_data *data)
|
||||
{
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
|
@ -1776,6 +1998,10 @@ static int uniwill_suspend(struct device *dev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = uniwill_suspend_touchpad_toggle(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = uniwill_suspend_battery(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -1795,36 +2021,35 @@ static int uniwill_resume_fn_lock(struct uniwill_data *data)
|
|||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK))
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS,
|
||||
data->last_status);
|
||||
return uniwill_write_fn_lock(data, data->last_fn_lock_state);
|
||||
}
|
||||
|
||||
static int uniwill_resume_super_key(struct uniwill_data *data)
|
||||
{
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_SUPER_KEY))
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return uniwill_write_super_key_enable(data, data->last_super_key_enable_state);
|
||||
}
|
||||
|
||||
if ((data->last_switch_status & SUPER_KEY_LOCK_STATUS) == (value & SUPER_KEY_LOCK_STATUS))
|
||||
static int uniwill_resume_touchpad_toggle(struct uniwill_data *data)
|
||||
{
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE))
|
||||
return 0;
|
||||
|
||||
return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK,
|
||||
TRIGGER_SUPER_KEY_LOCK);
|
||||
return uniwill_write_touchpad_toggle_enable(data, data->last_touchpad_toggle_enable_state);
|
||||
}
|
||||
|
||||
static int uniwill_resume_battery(struct uniwill_data *data)
|
||||
{
|
||||
if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
|
||||
return 0;
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES))
|
||||
return uniwill_restore_charge_type(data);
|
||||
|
||||
return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK,
|
||||
data->last_charge_ctrl);
|
||||
if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT))
|
||||
return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK,
|
||||
data->last_charge_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data)
|
||||
|
|
@ -1863,6 +2088,10 @@ static int uniwill_resume(struct device *dev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = uniwill_resume_touchpad_toggle(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = uniwill_resume_battery(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -1899,7 +2128,7 @@ static struct platform_driver uniwill_driver = {
|
|||
|
||||
static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_GPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
|
|
@ -1910,7 +2139,7 @@ static struct uniwill_device_descriptor lapac71h_descriptor __initdata = {
|
|||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
|
||||
UNIWILL_FEATURE_BATTERY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_GPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
|
|
@ -1922,7 +2151,7 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = {
|
|||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
|
||||
UNIWILL_FEATURE_LIGHTBAR |
|
||||
UNIWILL_FEATURE_BATTERY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_GPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
|
|
@ -1939,6 +2168,7 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = {
|
|||
static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
UNIWILL_FEATURE_SECONDARY_FAN |
|
||||
|
|
@ -1948,6 +2178,7 @@ static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata =
|
|||
static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_GPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
|
|
@ -1958,6 +2189,7 @@ static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __ini
|
|||
static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_GPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
|
|
@ -1969,6 +2201,7 @@ static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __ini
|
|||
static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
UNIWILL_FEATURE_SECONDARY_FAN,
|
||||
|
|
@ -1977,6 +2210,7 @@ static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata =
|
|||
static struct uniwill_device_descriptor tux_featureset_3_nvidia_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_GPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
|
|
@ -2002,6 +2236,7 @@ static int phxtxx1_probe(struct uniwill_data *data)
|
|||
static struct uniwill_device_descriptor phxtxx1_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
UNIWILL_FEATURE_USB_C_POWER_PRIORITY,
|
||||
|
|
@ -2027,6 +2262,7 @@ static int phxarx1_phxaqf1_probe(struct uniwill_data *data)
|
|||
static struct uniwill_device_descriptor phxarx1_phxaqf1_descriptor __initdata = {
|
||||
.features = UNIWILL_FEATURE_FN_LOCK |
|
||||
UNIWILL_FEATURE_SUPER_KEY |
|
||||
UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
|
||||
UNIWILL_FEATURE_CPU_TEMP |
|
||||
UNIWILL_FEATURE_PRIMARY_FAN |
|
||||
UNIWILL_FEATURE_SECONDARY_FAN |
|
||||
|
|
@ -2508,7 +2744,7 @@ static int __init uniwill_init(void)
|
|||
|
||||
if (force) {
|
||||
/* Assume that the device supports all features except the charge limit */
|
||||
device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY;
|
||||
device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT;
|
||||
pr_warn("Enabling potentially unsupported features\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block
|
|||
static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
|
||||
{
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
if (obj->type != ACPI_TYPE_INTEGER)
|
||||
return;
|
||||
|
|
@ -56,7 +57,9 @@ static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
|
|||
|
||||
dev_dbg(&wdev->dev, "Received WMI event %u\n", value);
|
||||
|
||||
blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL);
|
||||
ret = blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL);
|
||||
if (notifier_to_errno(ret) < 0)
|
||||
dev_err(&wdev->dev, "Failed to handle event %u\n", value);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/irq.h>
|
||||
|
|
@ -360,6 +362,61 @@ static const struct software_node *cherryview_gpiochip_node_group[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static void gpio_secondary_unset(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
set_secondary_fwnode(dev, NULL);
|
||||
}
|
||||
|
||||
static void gpio_secondary_unregister_node_group(void *data)
|
||||
{
|
||||
const struct software_node **nodes = data;
|
||||
|
||||
software_node_unregister_node_group(nodes);
|
||||
}
|
||||
|
||||
static int gpio_secondary_fwnode_init(struct device *parent)
|
||||
{
|
||||
const struct software_node *const *swnode;
|
||||
struct fwnode_handle *fwnode;
|
||||
int ret;
|
||||
|
||||
if (!gpiochip_node_group)
|
||||
return 0;
|
||||
|
||||
ret = software_node_register_node_group(gpiochip_node_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(parent,
|
||||
gpio_secondary_unregister_node_group,
|
||||
gpiochip_node_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (swnode = gpiochip_node_group; *swnode; swnode++) {
|
||||
struct device *dev __free(put_device) =
|
||||
acpi_bus_find_device_by_name((*swnode)->name);
|
||||
if (!dev)
|
||||
return dev_err_probe(parent,
|
||||
-ENODEV, "Failed to find the required GPIO controller: %s\n",
|
||||
(*swnode)->name);
|
||||
|
||||
fwnode = software_node_fwnode(*swnode);
|
||||
if (WARN_ON(!fwnode))
|
||||
return -ENOENT;
|
||||
|
||||
set_secondary_fwnode(dev, fwnode);
|
||||
|
||||
ret = devm_add_action_or_reset(parent, gpio_secondary_unset, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void x86_android_tablet_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -391,7 +448,6 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
|
|||
|
||||
software_node_unregister_node_group(gpio_button_swnodes);
|
||||
software_node_unregister_node_group(swnode_group);
|
||||
software_node_unregister_node_group(gpiochip_node_group);
|
||||
}
|
||||
|
||||
static __init int x86_android_tablet_probe(struct platform_device *pdev)
|
||||
|
|
@ -427,9 +483,11 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
|
|||
break;
|
||||
}
|
||||
|
||||
ret = software_node_register_node_group(gpiochip_node_group);
|
||||
if (ret)
|
||||
ret = gpio_secondary_fwnode_init(&pdev->dev);
|
||||
if (ret) {
|
||||
x86_android_tablet_remove(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = software_node_register_node_group(dev_info->swnode_group);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ static int atla10_ec_probe(struct i2c_client *client)
|
|||
}
|
||||
|
||||
static const struct i2c_device_id atla10_ec_id_table[] = {
|
||||
{ "vexia_atla10_ec" },
|
||||
{ .name = "vexia_atla10_ec" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MODULE_NAME "xo15-ebook"
|
||||
|
||||
|
|
@ -38,15 +39,16 @@ MODULE_DEVICE_TABLE(acpi, ebook_device_ids);
|
|||
struct ebook_switch {
|
||||
struct input_dev *input;
|
||||
char phys[32]; /* for input device */
|
||||
bool gpe_enabled;
|
||||
};
|
||||
|
||||
static int ebook_send_state(struct acpi_device *device)
|
||||
static int ebook_send_state(struct device *dev)
|
||||
{
|
||||
struct ebook_switch *button = acpi_driver_data(device);
|
||||
struct ebook_switch *button = dev_get_drvdata(dev);
|
||||
unsigned long long state;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state);
|
||||
status = acpi_evaluate_integer(ACPI_HANDLE(dev), "EBK", NULL, &state);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
|
|
@ -56,16 +58,15 @@ static int ebook_send_state(struct acpi_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ebook_switch_notify(struct acpi_device *device, u32 event)
|
||||
static void ebook_switch_notify(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case ACPI_FIXED_HARDWARE_EVENT:
|
||||
case XO15_EBOOK_NOTIFY_STATUS:
|
||||
ebook_send_state(device);
|
||||
ebook_send_state(data);
|
||||
break;
|
||||
default:
|
||||
acpi_handle_debug(device->handle,
|
||||
"Unsupported event [0x%x]\n", event);
|
||||
acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -73,37 +74,36 @@ static void ebook_switch_notify(struct acpi_device *device, u32 event)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ebook_switch_resume(struct device *dev)
|
||||
{
|
||||
return ebook_send_state(to_acpi_device(dev));
|
||||
return ebook_send_state(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume);
|
||||
|
||||
static int ebook_switch_add(struct acpi_device *device)
|
||||
static int ebook_switch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct acpi_device *device = ACPI_COMPANION(dev);
|
||||
const struct acpi_device_id *id;
|
||||
struct ebook_switch *button;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
button = kzalloc_obj(struct ebook_switch);
|
||||
button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL);
|
||||
if (!button)
|
||||
return -ENOMEM;
|
||||
|
||||
device->driver_data = button;
|
||||
platform_set_drvdata(pdev, button);
|
||||
|
||||
button->input = input = input_allocate_device();
|
||||
if (!input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_button;
|
||||
}
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
button->input = input;
|
||||
|
||||
id = acpi_match_acpi_device(ebook_device_ids, device);
|
||||
if (!id) {
|
||||
dev_err(&device->dev, "Unsupported hid\n");
|
||||
error = -ENODEV;
|
||||
goto err_free_input;
|
||||
}
|
||||
if (!id)
|
||||
return dev_err_probe(dev, -ENODEV, "Unsupported hid\n");
|
||||
|
||||
strscpy(acpi_device_name(device), XO15_EBOOK_DEVICE_NAME);
|
||||
strscpy(acpi_device_class(device), XO15_EBOOK_CLASS "/" XO15_EBOOK_SUBCLASS);
|
||||
|
|
@ -113,50 +113,51 @@ static int ebook_switch_add(struct acpi_device *device)
|
|||
input->name = acpi_device_name(device);
|
||||
input->phys = button->phys;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &device->dev;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_SW);
|
||||
set_bit(SW_TABLET_MODE, input->swbit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err_free_input;
|
||||
return error;
|
||||
|
||||
ebook_send_state(device);
|
||||
error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY,
|
||||
ebook_switch_notify, dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ebook_send_state(dev);
|
||||
|
||||
if (device->wakeup.flags.valid) {
|
||||
/* Button's GPE is run-wake GPE */
|
||||
acpi_enable_gpe(device->wakeup.gpe_device,
|
||||
device->wakeup.gpe_number);
|
||||
device_set_wakeup_enable(&device->dev, true);
|
||||
button->gpe_enabled = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_input:
|
||||
input_free_device(input);
|
||||
err_free_button:
|
||||
kfree(button);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void ebook_switch_remove(struct acpi_device *device)
|
||||
static void ebook_switch_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ebook_switch *button = acpi_driver_data(device);
|
||||
struct ebook_switch *button = platform_get_drvdata(pdev);
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
|
||||
input_unregister_device(button->input);
|
||||
kfree(button);
|
||||
if (button->gpe_enabled)
|
||||
acpi_disable_gpe(device->wakeup.gpe_device,
|
||||
device->wakeup.gpe_number);
|
||||
|
||||
acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY,
|
||||
ebook_switch_notify);
|
||||
}
|
||||
|
||||
static struct acpi_driver xo15_ebook_driver = {
|
||||
.name = MODULE_NAME,
|
||||
.class = XO15_EBOOK_CLASS,
|
||||
.ids = ebook_device_ids,
|
||||
.ops = {
|
||||
.add = ebook_switch_add,
|
||||
.remove = ebook_switch_remove,
|
||||
.notify = ebook_switch_notify,
|
||||
static struct platform_driver xo15_ebook_driver = {
|
||||
.probe = ebook_switch_probe,
|
||||
.remove = ebook_switch_remove,
|
||||
.driver = {
|
||||
.name = MODULE_NAME,
|
||||
.acpi_match_table = ebook_device_ids,
|
||||
.pm = &ebook_switch_pm,
|
||||
},
|
||||
.drv.pm = &ebook_switch_pm,
|
||||
};
|
||||
module_acpi_driver(xo15_ebook_driver);
|
||||
module_platform_driver(xo15_ebook_driver);
|
||||
|
|
|
|||
|
|
@ -798,6 +798,8 @@ int acpi_get_local_u64_address(acpi_handle handle, u64 *addr);
|
|||
int acpi_get_local_address(acpi_handle handle, u32 *addr);
|
||||
const char *acpi_get_subsystem_id(acpi_handle handle);
|
||||
|
||||
struct device *acpi_bus_find_device_by_name(const char *name);
|
||||
|
||||
#ifdef CONFIG_ACPI_MRRM
|
||||
int acpi_mrrm_max_mem_region(void);
|
||||
#endif
|
||||
|
|
@ -1106,6 +1108,11 @@ static inline const char *acpi_get_subsystem_id(acpi_handle handle)
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct device *acpi_bus_find_device_by_name(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int acpi_register_wakeup_handler(int wake_irq,
|
||||
bool (*wakeup)(void *context), void *context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0))
|
||||
#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3))
|
||||
#define TABLE_OFFSET_SHIFT 3
|
||||
#define PMT_DISC_DWORDS 4
|
||||
|
||||
struct device;
|
||||
struct pci_dev;
|
||||
|
|
@ -122,7 +123,7 @@ struct intel_vsec_platform_info {
|
|||
struct device *parent;
|
||||
struct intel_vsec_header **headers;
|
||||
const struct vsec_feature_dependency *deps;
|
||||
u32 (*acpi_disc)[4];
|
||||
u32 (*acpi_disc)[PMT_DISC_DWORDS];
|
||||
enum intel_vsec_disc_source src;
|
||||
void *priv_data;
|
||||
unsigned long caps;
|
||||
|
|
@ -135,8 +136,6 @@ struct intel_vsec_platform_info {
|
|||
* struct intel_vsec_device - Auxbus specific device information
|
||||
* @auxdev: auxbus device struct for auxbus access
|
||||
* @dev: struct device associated with the device
|
||||
* @resource: PCI discovery resources (BAR windows), one per discovery
|
||||
* instance. Valid only when @src == INTEL_VSEC_DISC_PCI
|
||||
* @acpi_disc: ACPI discovery tables, each entry is two QWORDs
|
||||
* in little-endian format as defined by the PMT ACPI spec.
|
||||
* Valid only when @src == INTEL_VSEC_DISC_ACPI.
|
||||
|
|
@ -149,12 +148,13 @@ struct intel_vsec_platform_info {
|
|||
* @quirks: specified quirks
|
||||
* @base_addr: base address of entries (if specified)
|
||||
* @cap_id: the enumerated id of the vsec feature
|
||||
* @resource: PCI discovery resources (BAR windows), one per discovery
|
||||
* instance. Valid only when @src == INTEL_VSEC_DISC_PCI
|
||||
*/
|
||||
struct intel_vsec_device {
|
||||
struct auxiliary_device auxdev;
|
||||
struct device *dev;
|
||||
struct resource *resource;
|
||||
u32 (*acpi_disc)[4];
|
||||
u32 (*acpi_disc)[PMT_DISC_DWORDS];
|
||||
enum intel_vsec_disc_source src;
|
||||
struct ida *ida;
|
||||
int num_resources;
|
||||
|
|
@ -164,6 +164,7 @@ struct intel_vsec_device {
|
|||
unsigned long quirks;
|
||||
u64 base_addr;
|
||||
unsigned long cap_id;
|
||||
struct resource resource[] __counted_by(num_resources);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -147,6 +147,13 @@
|
|||
#define ASUS_WMI_DEVID_GPU_MUX 0x00090016
|
||||
#define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026
|
||||
|
||||
/* Keystone dongle insert/remove state.
|
||||
* PRESENCE_BIT (0x00010000) encodes insert state:
|
||||
* 0x00010000 = inserted, 0x00000000 = absent. STATUS_BIT is never set.
|
||||
* 0xFFFFFFFE means no keystone slot on this machine.
|
||||
*/
|
||||
#define ASUS_WMI_DEVID_KEYSTONE 0x00120091
|
||||
|
||||
/* TUF laptop RGB modes/colours */
|
||||
#define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056
|
||||
#define ASUS_WMI_DEVID_TUF_RGB_MODE2 0x0010005A
|
||||
|
|
|
|||
|
|
@ -1280,6 +1280,8 @@ static void do_tee_entry(struct module *mod, void *symval)
|
|||
static void do_wmi_entry(struct module *mod, void *symval)
|
||||
{
|
||||
DEF_FIELD_ADDR(symval, wmi_device_id, guid_string);
|
||||
char result[sizeof(*guid_string)];
|
||||
int i;
|
||||
|
||||
if (strlen(*guid_string) != UUID_STRING_LEN) {
|
||||
warn("Invalid WMI device id 'wmi:%s' in '%s'\n",
|
||||
|
|
@ -1287,7 +1289,31 @@ static void do_wmi_entry(struct module *mod, void *symval)
|
|||
return;
|
||||
}
|
||||
|
||||
module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", *guid_string);
|
||||
for (i = 0; i < UUID_STRING_LEN; i++) {
|
||||
char value = (*guid_string)[i];
|
||||
bool valid = false;
|
||||
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
if (value == '-')
|
||||
valid = true;
|
||||
} else {
|
||||
if (isxdigit(value))
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
warn("Invalid character %c inside WMI GUID string '%s' in '%s'\n",
|
||||
value, *guid_string, mod->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Some GUIDs from BMOF definitions contain lowercase characters */
|
||||
result[i] = toupper(value);
|
||||
}
|
||||
|
||||
result[i] = '\0';
|
||||
|
||||
module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", result);
|
||||
}
|
||||
|
||||
/* Looks like: mhi:S */
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ static void daemonize(char *rundir, char *pidfile)
|
|||
{
|
||||
int pid, sid, i;
|
||||
char str[10];
|
||||
struct stat st;
|
||||
struct sigaction sig_actions;
|
||||
sigset_t sig_set;
|
||||
int ret;
|
||||
|
|
@ -200,11 +201,17 @@ static void daemonize(char *rundir, char *pidfile)
|
|||
if (ret == -1)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600);
|
||||
pid_file_handle = open(pidfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
|
||||
if (pid_file_handle == -1) {
|
||||
/* Couldn't open lock file */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fstat(pid_file_handle, &st) == -1)
|
||||
exit(1);
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
exit(1);
|
||||
/* Try to lock file */
|
||||
#ifdef LOCKF_SUPPORT
|
||||
if (lockf(pid_file_handle, F_TLOCK, 0) == -1) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user