platform-drivers-x86 for v6.4-1
Highlights: - AMD PMC and PMF drivers: - Numerous bugfixes - Intel Speed Select Technology (ISST): - TPMI (Topology Aware Register and PM Capsule Interface) support for ISST support on upcoming processor models - Various other improvements / new hw support - tools/intel-speed-select: TPMI support + other improvements - Intel In Field Scan (IFS): - Add Array Bist test support - New drivers: - intel_bytcrc_pwrsrc Crystal Cove PMIC pwrsrc / reset-reason driver - lenovo-ymc Yoga Mode Control driver for reporting SW_TABLET_MODE - msi-ec Driver for MSI laptop EC features like battery charging limits - apple-gmux: - Support for new MMIO based models (T2 Macs) - Honor acpi_backlight= auto-detect-code + kernel cmdline option to switch between gmux and apple_bl backlight drivers and remove own custom handling for this - x86-android-tablets: Refactor / cleanup + new hw support - Miscellaneous other cleanups / fixes The following is an automated git shortlog grouped by driver: Add driver for Yoga Tablet Mode switch: - Add driver for Yoga Tablet Mode switch Add intel_bytcrc_pwrsrc driver: - Add intel_bytcrc_pwrsrc driver Add new msi-ec driver: - Add new msi-ec driver Documentation/ABI: - Update IFS ABI doc ISST: - unlock on error path in tpmi_sst_init() - Add suspend/resume callbacks - Add SST-TF support via TPMI - Add SST-BF support via TPMI - Add SST-PP support via TPMI - Add SST-CP support via TPMI - Parse SST MMIO and update instance - Enumerate TPMI SST and create framework - Add support for MSR 0x54 - Add API version of the target - Add IOCTL default callback - Add TPMI target Merge remote-tracking branch 'intel-speed-select/intel-sst' into review-hans: - Merge remote-tracking branch 'intel-speed-select/intel-sst' into review-hans Merge tag 'ib-pdx86-backlight-6.4' into review-hans: - Merge tag 'ib-pdx86-backlight-6.4' into review-hans Move ideapad ACPI helpers to a new header: - Move ideapad ACPI helpers to a new header acer-wmi: - Convert to platform remove callback returning void acerhdf: - Remove unneeded semicolon adv_swbutton: - Convert to platform remove callback returning void amilo-rfkill: - Convert to platform remove callback returning void apple-gmux: - Fix iomem_base __iomem annotation - return -EFAULT if copy fails - Update apple_gmux_detect documentation - Add acpi_video_get_backlight_type() check - add debugfs interface - support MMIO gmux on T2 Macs - refactor gmux types - use first bit to check switch state backlight: - apple_bl: Use acpi_video_get_backlight_type() barco-p50-gpio: - Convert to platform remove callback returning void classmate: - mark SPI related data as maybe unused compal-laptop: - Convert to platform remove callback returning void dell: - dell-smo8800: Convert to platform remove callback returning void - dcdbas: Convert to platform remove callback returning void dell-laptop: - Register ctl-led for speaker-mute hp: - tc1100-wmi: Convert to platform remove callback returning void - hp_accel: Convert to platform remove callback returning void huawei-wmi: - Convert to platform remove callback returning void ideapad-laptop: - Convert to platform remove callback returning void intel: - vbtn: Convert to platform remove callback returning void - telemetry: pltdrv: Convert to platform remove callback returning void - pmc: core: Convert to platform remove callback returning void - mrfld_pwrbtn: Convert to platform remove callback returning void - int3472: discrete: Convert to platform remove callback returning void - int1092: intel_sar: Convert to platform remove callback returning void - int0002_vgpio: Convert to platform remove callback returning void - hid: Convert to platform remove callback returning void - chtwc_int33fe: Convert to platform remove callback returning void - chtdc_ti_pwrbtn: Convert to platform remove callback returning void - bxtwc_tmu: Convert to platform remove callback returning void intel-uncore-freq: - Add client processors mlxbf-bootctl: - Add sysfs file for BlueField boot fifo pcengines-apuv2: - Drop platform:pcengines-apuv2 module-alias platform/mellanox: - add firmware reset support platform/olpc: - olpc-xo175-ec: Use SPI device ID data to bind device platform/surface: - aggregator_registry: Add support for tablet-mode switch on Surface Pro 9 - aggregator_tabletsw: Add support for Type-Cover posture source - aggregator_tabletsw: Properly handle different posture source IDs platform/x86/amd: - pmc: provide user message where s0ix is not supported - pmc: Remove __maybe_unused from amd_pmc_suspend_handler() - pmc: Convert to platform remove callback returning void - pmc: Fix memory leak in amd_pmc_stb_debugfs_open_v2() - pmc: Move out of BIOS SMN pair for STB init - pmc: Utilize SMN index 0 for driver probe - pmc: Move idlemask check into `amd_pmc_idlemask_read` - pmc: Don't dump data after resume from s0i3 on picasso - pmc: Hide SMU version and program attributes for Picasso - pmc: Don't try to read SMU version on Picasso - pmf: core: Convert to platform remove callback returning void - hsmp: Convert to platform remove callback returning void platform/x86/amd/pmf: - Move out of BIOS SMN pair for driver probe platform/x86/intel: - vsec: Use intel_vsec_dev_release() to simplify init() error cleanup - vsec: Explicitly enable capabilities platform/x86/intel/ifs: - Update IFS doc - Implement Array BIST test - Sysfs interface for Array BIST - Introduce Array Scan test to IFS - IFS cleanup - Reorganize driver data - Separate ifs_pkg_auth from ifs_data platform/x86/intel/pmc/mtl: - Put GNA/IPU/VPU devices in D3 platform/x86/intel/pmt: - Ignore uninitialized entries - Add INTEL_PMT module namespace platform/x86/intel/sdsi: - Change mailbox timeout samsung-q10: - Convert to platform remove callback returning void serial-multi-instantiate: - Convert to platform remove callback returning void sony: - mark SPI related data as maybe unused think-lmi: - Remove unnecessary casts for attributes - Remove custom kobject sysfs_ops - Properly interpret return value of tlmi_setting thinkpad_acpi: - Fix Embedded Controller access on X380 Yoga tools/power/x86/intel-speed-select: - Update version - Change TRL display for Emerald Rapids - Identify Emerald Rapids - Display AMX base frequency - Use cgroup v2 isolation - Add missing free cpuset - Fix clos-max display with TPMI I/F - Add cpu id check - Avoid setting duplicate tdp level - Remove cpu mask display for non-cpu power domain - Hide invalid TRL level - Display fact info for non-cpu power domain - Show level 0 name for new api_version - Prevent cpu clos config for non-cpu power domain - Allow display non-cpu power domain info - Display amx_p1 and cooling_type - Display punit info - Introduce TPMI interface support - Get punit core mapping information - Introduce api_version helper - Support large clos_min/max - Introduce is_debug_enabled() - Allow api_version based platform callbacks - Move send_mbox_cmd to isst-core-mbox.c - Abstract adjust_uncore_freq - Abstract read_pm_config - Abstract clos_associate - Abstract clos_get_assoc_status - Abstract set_clos - Abstract pm_get_clos - Abstract pm_qos_config - Abstract get_clos_information - Abstract get_get_trls - Enhance get_tdp_info - Abstract get_uncore_p0_p1_info - Abstract get_fact_info - Abstract set_pbf_fact_status - Remove isst_get_pbf_info_complete - Abstract get_pbf_info - Abstract set_tdp_level - Abstract get_trl_bucket_info - Abstract get_get_trl - Abstract get_coremask_info - Abstract get_tjmax_info - Move code right before its caller - Abstract get_pwr_info - Abstract get_tdp_info - Abstract get_ctdp_control - Abstract get_config_levels - Abstract is_punit_valid - Introduce isst-core-mbox.c - Always invoke isst_fill_platform_info - Introduce isst_get_disp_freq_multiplier - Move mbox functions to isst-core.c - Improve isst_print_extended_platform_info - Rename for_each_online_package_in_set - Introduce support for multi-punit - Introduce isst_is_punit_valid() - Introduce punit to isst_id - Follow TRL nameing for FACT info - Unify TRL levels wmi: - Convert to platform remove callback returning void x86-android-tablets: - Add accelerometer support for Yoga Tablet 2 1050/830 series - Add "yogabook-touch-kbd-digitizer-switch" pdev for Lenovo Yoga Book - Add Wacom digitizer info for Lenovo Yoga Book - Update Yoga Book HiDeep touchscreen comment - Add Lenovo Yoga Book X90F/L data - Share lp855x_platform_data between different models - Use LP8557 in direct mode on both the Yoga 830 and the 1050 - Add depends on PMIC_OPREGION - Lenovo Yoga Book match is for YB1-X91 models only - Add LID switch support for Yoga Tablet 2 1050/830 series - Add backlight ctrl for Lenovo Yoga Tab 3 Pro YT3-X90F - Add touchscreen support for Lenovo Yoga Tab 3 Pro YT3-X90F - Add support for the Dolby button on Peaq C1010 - Add gpio_keys support to x86_android_tablet_init() - Move remaining tablets to other.c - Move Lenovo tablets to their own file - Move Asus tablets to their own file - Move shared power-supply fw-nodes to a separate file - Move DMI match table into its own dmi.c file - Move core code into new core.c file - Move into its own subdir - Add Acer Iconia One 7 B1-750 data x86/include/asm/msr-index.h: - Add IFS Array test bits xo1-rfkill: - Convert to platform remove callback returning void -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEEuvA7XScYQRpenhd+kuxHeUQDJ9wFAmRGmK4UHGhkZWdvZWRl QHJlZGhhdC5jb20ACgkQkuxHeUQDJ9yBCAf+PebzfccC2ABHq+nFGSok18beRtFf fGs9NI21Mjdbhhy+KsKddgZceh7pbdcaIznuka3TZAK0UXcHRe30X3eoDvSCk9YW Xj/Uf3ExsipNh1Ung+Q1qTWtzUw7XdJWqMZ5HxlUI2ZlmSTAIOyZBpSEPrK052oi lAbSqrnB1DEh1qYV4Q7g71R82iAR791DAH1dsDZwC1Zb6KK6fxI/zQhw4JP1XSCs htE5RFUzPWiXG2ou5t6Nteju/QqEaCoIS7z7ZK/SgWcLlPxeksxwso3obI/U8PvD JMmMiY4VFzizuGqTZHiy/EtKXo1pq+fOcMEqSuaaDfcYgdHmLm0OIU12Ig== =51xc -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v6.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 Pull x86 platform driver updates from Hans de Goede: - AMD PMC and PMF drivers: - Numerous bugfixes - Intel Speed Select Technology (ISST): - TPMI (Topology Aware Register and PM Capsule Interface) support for ISST support on upcoming processor models - Various other improvements / new hw support - tools/intel-speed-select: TPMI support + other improvements - Intel In Field Scan (IFS): - Add Array Bist test support - New drivers: - intel_bytcrc_pwrsrc Crystal Cove PMIC pwrsrc / reset-reason driver - lenovo-ymc Yoga Mode Control driver for reporting SW_TABLET_MODE - msi-ec Driver for MSI laptop EC features like battery charging limits - apple-gmux: - Support for new MMIO based models (T2 Macs) - Honor acpi_backlight= auto-detect-code + kernel cmdline option to switch between gmux and apple_bl backlight drivers and remove own custom handling for this - x86-android-tablets: Refactor / cleanup + new hw support - Miscellaneous other cleanups / fixes * tag 'platform-drivers-x86-v6.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (178 commits) platform/x86: x86-android-tablets: Add accelerometer support for Yoga Tablet 2 1050/830 series platform/x86: x86-android-tablets: Add "yogabook-touch-kbd-digitizer-switch" pdev for Lenovo Yoga Book platform/x86: x86-android-tablets: Add Wacom digitizer info for Lenovo Yoga Book platform/x86: x86-android-tablets: Update Yoga Book HiDeep touchscreen comment platform/x86: thinkpad_acpi: Fix Embedded Controller access on X380 Yoga platform/x86/intel/sdsi: Change mailbox timeout platform/x86/intel/pmt: Ignore uninitialized entries platform/x86: amd: pmc: provide user message where s0ix is not supported platform/x86/amd: pmc: Fix memory leak in amd_pmc_stb_debugfs_open_v2() mlxbf-bootctl: Add sysfs file for BlueField boot fifo platform/x86: amd: pmc: Remove __maybe_unused from amd_pmc_suspend_handler() platform/x86/intel/pmc/mtl: Put GNA/IPU/VPU devices in D3 platform/x86/amd: pmc: Move out of BIOS SMN pair for STB init platform/x86/amd: pmc: Utilize SMN index 0 for driver probe platform/x86/amd: pmc: Move idlemask check into `amd_pmc_idlemask_read` platform/x86/amd: pmc: Don't dump data after resume from s0i3 on picasso platform/x86/amd: pmc: Hide SMU version and program attributes for Picasso platform/x86/amd: pmc: Don't try to read SMU version on Picasso platform/x86/amd/pmf: Move out of BIOS SMN pair for driver probe platform/x86: intel-uncore-freq: Add client processors ...
This commit is contained in:
commit
088e0c1885
@ -1,3 +1,7 @@
|
||||
Device instance to test mapping
|
||||
intel_ifs_0 -> Scan Test
|
||||
intel_ifs_1 -> Array BIST test
|
||||
|
||||
What: /sys/devices/virtual/misc/intel_ifs_<N>/run_test
|
||||
Date: Nov 16 2022
|
||||
KernelVersion: 6.2
|
||||
@ -8,6 +12,7 @@ Description: Write <cpu#> to trigger IFS test for one online core.
|
||||
completes the test for the core containing that thread.
|
||||
Example: to test the core containing cpu5: echo 5 >
|
||||
/sys/devices/virtual/misc/intel_ifs_<N>/run_test
|
||||
Devices: all
|
||||
|
||||
What: /sys/devices/virtual/misc/intel_ifs_<N>/status
|
||||
Date: Nov 16 2022
|
||||
@ -15,21 +20,25 @@ KernelVersion: 6.2
|
||||
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
|
||||
Description: The status of the last test. It can be one of "pass", "fail"
|
||||
or "untested".
|
||||
Devices: all
|
||||
|
||||
What: /sys/devices/virtual/misc/intel_ifs_<N>/details
|
||||
Date: Nov 16 2022
|
||||
KernelVersion: 6.2
|
||||
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
|
||||
Description: Additional information regarding the last test. The details file reports
|
||||
the hex value of the SCAN_STATUS MSR. Note that the error_code field
|
||||
the hex value of the STATUS MSR for this test. Note that the error_code field
|
||||
may contain driver defined software code not defined in the Intel SDM.
|
||||
Devices: all
|
||||
|
||||
What: /sys/devices/virtual/misc/intel_ifs_<N>/image_version
|
||||
Date: Nov 16 2022
|
||||
KernelVersion: 6.2
|
||||
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
|
||||
Description: Version (hexadecimal) of loaded IFS binary image. If no scan image
|
||||
is loaded reports "none".
|
||||
Description: Version (hexadecimal) of loaded IFS test image. If no test image
|
||||
is loaded reports "none". Only present for device instances where a test image
|
||||
is applicable.
|
||||
Devices: intel_ifs_0
|
||||
|
||||
What: /sys/devices/virtual/misc/intel_ifs_<N>/current_batch
|
||||
Date: Nov 16 2022
|
||||
@ -39,3 +48,5 @@ Description: Write a number less than or equal to 0xff to load an IFS test image
|
||||
The number written treated as the 2 digit suffix in the following file name:
|
||||
/lib/firmware/intel/ifs_<N>/ff-mm-ss-02x.scan
|
||||
Reading the file will provide the suffix of the currently loaded IFS test image.
|
||||
This file is present only for device instances where a test image is applicable.
|
||||
Devices: intel_ifs_0
|
||||
|
@ -68,3 +68,10 @@ Description:
|
||||
Wasted burnt and invalid
|
||||
Invalid not burnt but marked as valid (error state).
|
||||
======= ===============================================
|
||||
|
||||
What: /sys/bus/platform/devices/MLNXBF04:00/bootfifo
|
||||
Date: Apr 2023
|
||||
KernelVersion: 6.4
|
||||
Contact: "Liming Sun <limings@nvidia.com>"
|
||||
Description:
|
||||
The file used to access the BlueField boot fifo.
|
||||
|
15
MAINTAINERS
15
MAINTAINERS
@ -14148,6 +14148,13 @@ S: Odd Fixes
|
||||
F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
|
||||
F: drivers/net/ieee802154/mrf24j40.c
|
||||
|
||||
MSI EC DRIVER
|
||||
M: Nikita Kravets <teackot@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
W: https://github.com/BeardOverflow/msi-ec
|
||||
F: drivers/platform/x86/msi-ec.*
|
||||
|
||||
MSI LAPTOP SUPPORT
|
||||
M: "Lee, Chun-Yi" <jlee@suse.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -16330,12 +16337,6 @@ S: Maintained
|
||||
F: crypto/pcrypt.c
|
||||
F: include/crypto/pcrypt.h
|
||||
|
||||
PEAQ WMI HOTKEYS DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/peaq-wmi.c
|
||||
|
||||
PECI HARDWARE MONITORING DRIVERS
|
||||
M: Iwona Winiarska <iwona.winiarska@intel.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -22725,7 +22726,7 @@ M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
|
||||
F: drivers/platform/x86/x86-android-tablets.c
|
||||
F: drivers/platform/x86/x86-android-tablets/
|
||||
|
||||
X86 PLATFORM DRIVERS
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
|
@ -206,6 +206,8 @@
|
||||
|
||||
/* Abbreviated from Intel SDM name IA32_INTEGRITY_CAPABILITIES */
|
||||
#define MSR_INTEGRITY_CAPS 0x000002d9
|
||||
#define MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT 2
|
||||
#define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT)
|
||||
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4
|
||||
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT)
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
@ -44,6 +45,10 @@ static const char * const mlxbf_bootctl_lifecycle_states[] = {
|
||||
[3] = "RMA",
|
||||
};
|
||||
|
||||
/* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */
|
||||
static void __iomem *mlxbf_rsh_boot_data;
|
||||
static void __iomem *mlxbf_rsh_boot_cnt;
|
||||
|
||||
/* ARM SMC call which is atomic and no need for lock. */
|
||||
static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
|
||||
{
|
||||
@ -244,11 +249,29 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev,
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
static ssize_t fw_reset_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long key;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 16, &key);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, key) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(post_reset_wdog);
|
||||
static DEVICE_ATTR_RW(reset_action);
|
||||
static DEVICE_ATTR_RW(second_reset_action);
|
||||
static DEVICE_ATTR_RO(lifecycle_state);
|
||||
static DEVICE_ATTR_RO(secure_boot_fuse_state);
|
||||
static DEVICE_ATTR_WO(fw_reset);
|
||||
|
||||
static struct attribute *mlxbf_bootctl_attrs[] = {
|
||||
&dev_attr_post_reset_wdog.attr,
|
||||
@ -256,6 +279,7 @@ static struct attribute *mlxbf_bootctl_attrs[] = {
|
||||
&dev_attr_second_reset_action.attr,
|
||||
&dev_attr_lifecycle_state.attr,
|
||||
&dev_attr_secure_boot_fuse_state.attr,
|
||||
&dev_attr_fw_reset.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -268,6 +292,45 @@ static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
|
||||
|
||||
static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long timeout = msecs_to_jiffies(500);
|
||||
unsigned long expire = jiffies + timeout;
|
||||
u64 data, cnt = 0;
|
||||
char *p = buf;
|
||||
|
||||
while (count >= sizeof(data)) {
|
||||
/* Give up reading if no more data within 500ms. */
|
||||
if (!cnt) {
|
||||
cnt = readq(mlxbf_rsh_boot_cnt);
|
||||
if (!cnt) {
|
||||
if (time_after(jiffies, expire))
|
||||
break;
|
||||
usleep_range(10, 50);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
data = readq(mlxbf_rsh_boot_data);
|
||||
memcpy(p, &data, sizeof(data));
|
||||
count -= sizeof(data);
|
||||
p += sizeof(data);
|
||||
cnt--;
|
||||
expire = jiffies + timeout;
|
||||
}
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = {
|
||||
.attr = { .name = "bootfifo", .mode = 0400 },
|
||||
.read = mlxbf_bootctl_bootfifo_read,
|
||||
};
|
||||
|
||||
static bool mlxbf_bootctl_guid_match(const guid_t *guid,
|
||||
const struct arm_smccc_res *res)
|
||||
{
|
||||
@ -285,6 +348,16 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev)
|
||||
guid_t guid;
|
||||
int ret;
|
||||
|
||||
/* Get the resource of the bootfifo data register. */
|
||||
mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(mlxbf_rsh_boot_data))
|
||||
return PTR_ERR(mlxbf_rsh_boot_data);
|
||||
|
||||
/* Get the resource of the bootfifo counter register. */
|
||||
mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(mlxbf_rsh_boot_cnt))
|
||||
return PTR_ERR(mlxbf_rsh_boot_cnt);
|
||||
|
||||
/* Ensure we have the UUID we expect for this service. */
|
||||
arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
|
||||
@ -302,11 +375,25 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
|
||||
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj,
|
||||
&mlxbf_bootctl_bootfifo_sysfs_attr);
|
||||
if (ret)
|
||||
pr_err("Unable to create bootfifo sysfs file, error %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlxbf_bootctl_remove(struct platform_device *pdev)
|
||||
{
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj,
|
||||
&mlxbf_bootctl_bootfifo_sysfs_attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mlxbf_bootctl_driver = {
|
||||
.probe = mlxbf_bootctl_probe,
|
||||
.remove = mlxbf_bootctl_remove,
|
||||
.driver = {
|
||||
.name = "mlxbf-bootctl",
|
||||
.dev_groups = mlxbf_bootctl_groups,
|
||||
|
@ -75,6 +75,12 @@
|
||||
|
||||
#define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008
|
||||
|
||||
/*
|
||||
* Initiate Firmware Reset via TYU. This might be invoked during the reset
|
||||
* flow in isolation mode.
|
||||
*/
|
||||
#define MLXBF_BOOTCTL_FW_RESET 0x8200000D
|
||||
|
||||
/* SMC function IDs for SiP Service queries */
|
||||
#define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00
|
||||
#define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01
|
||||
|
@ -746,6 +746,7 @@ static struct spi_driver olpc_xo175_ec_spi_driver = {
|
||||
.of_match_table = olpc_xo175_ec_of_match,
|
||||
.pm = &olpc_xo175_ec_pm_ops,
|
||||
},
|
||||
.id_table = olpc_xo175_ec_id_table,
|
||||
.probe = olpc_xo175_ec_probe,
|
||||
.remove = olpc_xo175_ec_remove,
|
||||
};
|
||||
|
@ -305,7 +305,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_pprof,
|
||||
/* TODO: Tablet mode switch (via POS subsystem) */
|
||||
&ssam_node_pos_tablet_switch,
|
||||
&ssam_node_hid_kip_keyboard,
|
||||
&ssam_node_hid_kip_penstash,
|
||||
&ssam_node_hid_kip_touchpad,
|
||||
|
@ -20,16 +20,23 @@
|
||||
|
||||
struct ssam_tablet_sw;
|
||||
|
||||
struct ssam_tablet_sw_state {
|
||||
u32 source;
|
||||
u32 state;
|
||||
};
|
||||
|
||||
struct ssam_tablet_sw_ops {
|
||||
int (*get_state)(struct ssam_tablet_sw *sw, u32 *state);
|
||||
const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state);
|
||||
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state);
|
||||
int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state);
|
||||
const char *(*state_name)(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state);
|
||||
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state);
|
||||
};
|
||||
|
||||
struct ssam_tablet_sw {
|
||||
struct ssam_device *sdev;
|
||||
|
||||
u32 state;
|
||||
struct ssam_tablet_sw_state state;
|
||||
struct work_struct update_work;
|
||||
struct input_dev *mode_switch;
|
||||
|
||||
@ -45,9 +52,11 @@ struct ssam_tablet_sw_desc {
|
||||
|
||||
struct {
|
||||
u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event);
|
||||
int (*get_state)(struct ssam_tablet_sw *sw, u32 *state);
|
||||
const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state);
|
||||
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state);
|
||||
int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state);
|
||||
const char *(*state_name)(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state);
|
||||
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state);
|
||||
} ops;
|
||||
|
||||
struct {
|
||||
@ -61,7 +70,7 @@ struct ssam_tablet_sw_desc {
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ssam_tablet_sw *sw = dev_get_drvdata(dev);
|
||||
const char *state = sw->ops.state_name(sw, sw->state);
|
||||
const char *state = sw->ops.state_name(sw, &sw->state);
|
||||
|
||||
return sysfs_emit(buf, "%s\n", state);
|
||||
}
|
||||
@ -79,19 +88,19 @@ static const struct attribute_group ssam_tablet_sw_group = {
|
||||
static void ssam_tablet_sw_update_workfn(struct work_struct *work)
|
||||
{
|
||||
struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work);
|
||||
struct ssam_tablet_sw_state state;
|
||||
int tablet, status;
|
||||
u32 state;
|
||||
|
||||
status = sw->ops.get_state(sw, &state);
|
||||
if (status)
|
||||
return;
|
||||
|
||||
if (sw->state == state)
|
||||
if (sw->state.source == state.source && sw->state.state == state.state)
|
||||
return;
|
||||
sw->state = state;
|
||||
|
||||
/* Send SW_TABLET_MODE event. */
|
||||
tablet = sw->ops.state_is_tablet_mode(sw, state);
|
||||
tablet = sw->ops.state_is_tablet_mode(sw, &state);
|
||||
input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
|
||||
input_sync(sw->mode_switch);
|
||||
}
|
||||
@ -146,7 +155,7 @@ static int ssam_tablet_sw_probe(struct ssam_device *sdev)
|
||||
sw->mode_switch->id.bustype = BUS_HOST;
|
||||
sw->mode_switch->dev.parent = &sdev->dev;
|
||||
|
||||
tablet = sw->ops.state_is_tablet_mode(sw, sw->state);
|
||||
tablet = sw->ops.state_is_tablet_mode(sw, &sw->state);
|
||||
input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE);
|
||||
input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
|
||||
|
||||
@ -203,9 +212,10 @@ enum ssam_kip_cover_state {
|
||||
SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05,
|
||||
};
|
||||
|
||||
static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state)
|
||||
static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state)
|
||||
{
|
||||
switch (state) {
|
||||
switch (state->state) {
|
||||
case SSAM_KIP_COVER_STATE_DISCONNECTED:
|
||||
return "disconnected";
|
||||
|
||||
@ -222,14 +232,15 @@ static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 stat
|
||||
return "folded-back";
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state);
|
||||
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state->state);
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state)
|
||||
static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state)
|
||||
{
|
||||
switch (state) {
|
||||
switch (state->state) {
|
||||
case SSAM_KIP_COVER_STATE_DISCONNECTED:
|
||||
case SSAM_KIP_COVER_STATE_FOLDED_CANVAS:
|
||||
case SSAM_KIP_COVER_STATE_FOLDED_BACK:
|
||||
@ -240,7 +251,7 @@ static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 s
|
||||
return false;
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", sw->state);
|
||||
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", state->state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -252,7 +263,7 @@ SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, {
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state)
|
||||
static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state)
|
||||
{
|
||||
int status;
|
||||
u8 raw;
|
||||
@ -263,7 +274,8 @@ static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state)
|
||||
return status;
|
||||
}
|
||||
|
||||
*state = raw;
|
||||
state->source = 0; /* Unused for KIP switch. */
|
||||
state->state = raw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -312,11 +324,24 @@ MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device
|
||||
#define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03
|
||||
#define SSAM_POS_MAX_SOURCES 4
|
||||
|
||||
enum ssam_pos_state {
|
||||
SSAM_POS_POSTURE_LID_CLOSED = 0x00,
|
||||
SSAM_POS_POSTURE_LAPTOP = 0x01,
|
||||
SSAM_POS_POSTURE_SLATE = 0x02,
|
||||
SSAM_POS_POSTURE_TABLET = 0x03,
|
||||
enum ssam_pos_source_id {
|
||||
SSAM_POS_SOURCE_COVER = 0x00,
|
||||
SSAM_POS_SOURCE_SLS = 0x03,
|
||||
};
|
||||
|
||||
enum ssam_pos_state_cover {
|
||||
SSAM_POS_COVER_DISCONNECTED = 0x01,
|
||||
SSAM_POS_COVER_CLOSED = 0x02,
|
||||
SSAM_POS_COVER_LAPTOP = 0x03,
|
||||
SSAM_POS_COVER_FOLDED_CANVAS = 0x04,
|
||||
SSAM_POS_COVER_FOLDED_BACK = 0x05,
|
||||
};
|
||||
|
||||
enum ssam_pos_state_sls {
|
||||
SSAM_POS_SLS_LID_CLOSED = 0x00,
|
||||
SSAM_POS_SLS_LAPTOP = 0x01,
|
||||
SSAM_POS_SLS_SLATE = 0x02,
|
||||
SSAM_POS_SLS_TABLET = 0x03,
|
||||
};
|
||||
|
||||
struct ssam_sources_list {
|
||||
@ -324,42 +349,116 @@ struct ssam_sources_list {
|
||||
__le32 id[SSAM_POS_MAX_SOURCES];
|
||||
} __packed;
|
||||
|
||||
static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, u32 state)
|
||||
static const char *ssam_pos_state_name_cover(struct ssam_tablet_sw *sw, u32 state)
|
||||
{
|
||||
switch (state) {
|
||||
case SSAM_POS_POSTURE_LID_CLOSED:
|
||||
case SSAM_POS_COVER_DISCONNECTED:
|
||||
return "disconnected";
|
||||
|
||||
case SSAM_POS_COVER_CLOSED:
|
||||
return "closed";
|
||||
|
||||
case SSAM_POS_POSTURE_LAPTOP:
|
||||
case SSAM_POS_COVER_LAPTOP:
|
||||
return "laptop";
|
||||
|
||||
case SSAM_POS_POSTURE_SLATE:
|
||||
return "slate";
|
||||
case SSAM_POS_COVER_FOLDED_CANVAS:
|
||||
return "folded-canvas";
|
||||
|
||||
case SSAM_POS_POSTURE_TABLET:
|
||||
return "tablet";
|
||||
case SSAM_POS_COVER_FOLDED_BACK:
|
||||
return "folded-back";
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state);
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state);
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state)
|
||||
static const char *ssam_pos_state_name_sls(struct ssam_tablet_sw *sw, u32 state)
|
||||
{
|
||||
switch (state) {
|
||||
case SSAM_POS_POSTURE_LAPTOP:
|
||||
case SSAM_POS_POSTURE_LID_CLOSED:
|
||||
case SSAM_POS_SLS_LID_CLOSED:
|
||||
return "closed";
|
||||
|
||||
case SSAM_POS_SLS_LAPTOP:
|
||||
return "laptop";
|
||||
|
||||
case SSAM_POS_SLS_SLATE:
|
||||
return "slate";
|
||||
|
||||
case SSAM_POS_SLS_TABLET:
|
||||
return "tablet";
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state);
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state)
|
||||
{
|
||||
switch (state->source) {
|
||||
case SSAM_POS_SOURCE_COVER:
|
||||
return ssam_pos_state_name_cover(sw, state->state);
|
||||
|
||||
case SSAM_POS_SOURCE_SLS:
|
||||
return ssam_pos_state_name_sls(sw, state->state);
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source);
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssam_pos_state_is_tablet_mode_cover(struct ssam_tablet_sw *sw, u32 state)
|
||||
{
|
||||
switch (state) {
|
||||
case SSAM_POS_COVER_DISCONNECTED:
|
||||
case SSAM_POS_COVER_FOLDED_CANVAS:
|
||||
case SSAM_POS_COVER_FOLDED_BACK:
|
||||
return true;
|
||||
|
||||
case SSAM_POS_COVER_CLOSED:
|
||||
case SSAM_POS_COVER_LAPTOP:
|
||||
return false;
|
||||
|
||||
case SSAM_POS_POSTURE_SLATE:
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssam_pos_state_is_tablet_mode_sls(struct ssam_tablet_sw *sw, u32 state)
|
||||
{
|
||||
switch (state) {
|
||||
case SSAM_POS_SLS_LAPTOP:
|
||||
case SSAM_POS_SLS_LID_CLOSED:
|
||||
return false;
|
||||
|
||||
case SSAM_POS_SLS_SLATE:
|
||||
return tablet_mode_in_slate_state;
|
||||
|
||||
case SSAM_POS_POSTURE_TABLET:
|
||||
case SSAM_POS_SLS_TABLET:
|
||||
return true;
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state);
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw,
|
||||
const struct ssam_tablet_sw_state *state)
|
||||
{
|
||||
switch (state->source) {
|
||||
case SSAM_POS_SOURCE_COVER:
|
||||
return ssam_pos_state_is_tablet_mode_cover(sw, state->state);
|
||||
|
||||
case SSAM_POS_SOURCE_SLS:
|
||||
return ssam_pos_state_is_tablet_mode_sls(sw, state->state);
|
||||
|
||||
default:
|
||||
dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -450,9 +549,10 @@ static int ssam_pos_get_posture_for_source(struct ssam_tablet_sw *sw, u32 source
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state)
|
||||
static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state)
|
||||
{
|
||||
u32 source_id;
|
||||
u32 source_state;
|
||||
int status;
|
||||
|
||||
status = ssam_pos_get_source(sw, &source_id);
|
||||
@ -461,13 +561,15 @@ static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state)
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ssam_pos_get_posture_for_source(sw, source_id, state);
|
||||
status = ssam_pos_get_posture_for_source(sw, source_id, &source_state);
|
||||
if (status) {
|
||||
dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n",
|
||||
source_id, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
state->source = source_id;
|
||||
state->state = source_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -84,13 +84,6 @@ config MXM_WMI
|
||||
MXM is a standard for laptop graphics cards, the WMI interface
|
||||
is required for switchable nvidia graphics machines
|
||||
|
||||
config PEAQ_WMI
|
||||
tristate "PEAQ 2-in-1 WMI hotkey driver"
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
help
|
||||
Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s.
|
||||
|
||||
config NVIDIA_WMI_EC_BACKLIGHT
|
||||
tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems"
|
||||
depends on ACPI_VIDEO
|
||||
@ -213,7 +206,6 @@ config APPLE_GMUX
|
||||
depends on ACPI && PCI
|
||||
depends on PNP
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
|
||||
help
|
||||
This driver provides support for the gmux device found on many
|
||||
Apple laptops, which controls the display mux for the hybrid
|
||||
@ -469,6 +461,15 @@ config IDEAPAD_LAPTOP
|
||||
This is a driver for Lenovo IdeaPad netbooks contains drivers for
|
||||
rfkill switch, hotkey, fan control and backlight control.
|
||||
|
||||
config LENOVO_YMC
|
||||
tristate "Lenovo Yoga Tablet Mode Control"
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
help
|
||||
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
|
||||
events for Lenovo Yoga notebooks.
|
||||
|
||||
config SENSORS_HDAPS
|
||||
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
|
||||
depends on INPUT
|
||||
@ -643,6 +644,14 @@ config THINKPAD_LMI
|
||||
|
||||
source "drivers/platform/x86/intel/Kconfig"
|
||||
|
||||
config MSI_EC
|
||||
tristate "MSI EC Extras"
|
||||
depends on ACPI
|
||||
depends on ACPI_BATTERY
|
||||
help
|
||||
This driver allows various MSI laptops' functionalities to be
|
||||
controlled from userspace, including battery charge threshold.
|
||||
|
||||
config MSI_LAPTOP
|
||||
tristate "MSI Laptop Extras"
|
||||
depends on ACPI
|
||||
@ -978,22 +987,7 @@ config TOUCHSCREEN_DMI
|
||||
the OS-image for the device. This option supplies the missing info.
|
||||
Enable this for x86 tablets with Silead or Chipone touchscreens.
|
||||
|
||||
config X86_ANDROID_TABLETS
|
||||
tristate "X86 Android tablet support"
|
||||
depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB
|
||||
help
|
||||
X86 tablets which ship with Android as (part of) the factory image
|
||||
typically have various problems with their DSDTs. The factory kernels
|
||||
shipped on these devices typically have device addresses and GPIOs
|
||||
hardcoded in the kernel, rather than specified in their DSDT.
|
||||
|
||||
With the DSDT containing a random collection of devices which may or
|
||||
may not actually be present. This driver contains various fixes for
|
||||
such tablets, including instantiating kernel devices for devices which
|
||||
are missing from the DSDT.
|
||||
|
||||
If you have a x86 Android tablet say Y or M here, for a generic x86
|
||||
distro config say M here.
|
||||
source "drivers/platform/x86/x86-android-tablets/Kconfig"
|
||||
|
||||
config FW_ATTR_CLASS
|
||||
tristate
|
||||
|
@ -12,7 +12,6 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
|
||||
obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
|
||||
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
|
||||
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
|
||||
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
|
||||
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
|
||||
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
|
||||
obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o
|
||||
@ -63,6 +62,7 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o
|
||||
# IBM Thinkpad and Lenovo
|
||||
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
||||
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
|
||||
obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o
|
||||
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
|
||||
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
|
||||
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
|
||||
@ -71,6 +71,7 @@ obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
|
||||
obj-y += intel/
|
||||
|
||||
# MSI
|
||||
obj-$(CONFIG_MSI_EC) += msi-ec.o
|
||||
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
||||
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
|
||||
|
||||
@ -112,7 +113,7 @@ obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o
|
||||
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
|
||||
obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
|
||||
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
|
||||
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets/
|
||||
|
||||
# Intel uncore drivers
|
||||
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
|
||||
|
@ -2258,7 +2258,7 @@ error_mailled:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int acer_platform_remove(struct platform_device *device)
|
||||
static void acer_platform_remove(struct platform_device *device)
|
||||
{
|
||||
if (has_cap(ACER_CAP_MAILLED))
|
||||
acer_led_exit();
|
||||
@ -2266,7 +2266,6 @@ static int acer_platform_remove(struct platform_device *device)
|
||||
acer_backlight_exit();
|
||||
|
||||
acer_rfkill_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -2334,7 +2333,7 @@ static struct platform_driver acer_platform_driver = {
|
||||
.pm = &acer_pm,
|
||||
},
|
||||
.probe = acer_platform_probe,
|
||||
.remove = acer_platform_remove,
|
||||
.remove_new = acer_platform_remove,
|
||||
.shutdown = acer_platform_shutdown,
|
||||
};
|
||||
|
||||
|
@ -341,7 +341,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
|
||||
pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n",
|
||||
fanoff, fanon, fanon);
|
||||
fanoff = fanon;
|
||||
};
|
||||
}
|
||||
|
||||
trips[0].temperature = fanon;
|
||||
trips[0].hysteresis = fanon - fanoff;
|
||||
|
@ -90,14 +90,12 @@ static int adv_swbutton_probe(struct platform_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv_swbutton_remove(struct platform_device *device)
|
||||
static void adv_swbutton_remove(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY,
|
||||
adv_swbutton_notify);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id button_device_ids[] = {
|
||||
@ -112,7 +110,7 @@ static struct platform_driver adv_swbutton_driver = {
|
||||
.acpi_match_table = button_device_ids,
|
||||
},
|
||||
.probe = adv_swbutton_probe,
|
||||
.remove = adv_swbutton_remove,
|
||||
.remove_new = adv_swbutton_remove,
|
||||
};
|
||||
module_platform_driver(adv_swbutton_driver);
|
||||
|
||||
|
@ -7,7 +7,7 @@ source "drivers/platform/x86/amd/pmf/Kconfig"
|
||||
|
||||
config AMD_PMC
|
||||
tristate "AMD SoC PMC driver"
|
||||
depends on ACPI && PCI && RTC_CLASS
|
||||
depends on ACPI && PCI && RTC_CLASS && AMD_NB
|
||||
select SERIO
|
||||
help
|
||||
The driver provides support for AMD Power Management Controller
|
||||
|
@ -340,16 +340,14 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev)
|
||||
return misc_register(&hsmp_device);
|
||||
}
|
||||
|
||||
static int hsmp_pltdrv_remove(struct platform_device *pdev)
|
||||
static void hsmp_pltdrv_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&hsmp_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver amd_hsmp_driver = {
|
||||
.probe = hsmp_pltdrv_probe,
|
||||
.remove = hsmp_pltdrv_remove,
|
||||
.remove_new = hsmp_pltdrv_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/amd_nb.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
@ -37,8 +38,6 @@
|
||||
#define AMD_PMC_SCRATCH_REG_YC 0xD14
|
||||
|
||||
/* STB Registers */
|
||||
#define AMD_PMC_STB_INDEX_ADDRESS 0xF8
|
||||
#define AMD_PMC_STB_INDEX_DATA 0xFC
|
||||
#define AMD_PMC_STB_PMI_0 0x03E30600
|
||||
#define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001
|
||||
#define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002
|
||||
@ -56,8 +55,6 @@
|
||||
#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000
|
||||
|
||||
/* Base address of SMU for mapping physical address to virtual address */
|
||||
#define AMD_PMC_SMU_INDEX_ADDRESS 0xB8
|
||||
#define AMD_PMC_SMU_INDEX_DATA 0xBC
|
||||
#define AMD_PMC_MAPPING_SIZE 0x01000
|
||||
#define AMD_PMC_BASE_ADDR_OFFSET 0x10000
|
||||
#define AMD_PMC_BASE_ADDR_LO 0x13B102E8
|
||||
@ -97,6 +94,7 @@
|
||||
#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 PMC_MSG_DELAY_MIN_US 50
|
||||
#define RESPONSE_REGISTER_LOOP_MAX 20000
|
||||
@ -268,6 +266,7 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
|
||||
dev->msg_port = 0;
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -342,33 +341,6 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
|
||||
struct seq_file *s)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
switch (pdev->cpu_id) {
|
||||
case AMD_CPU_ID_CZN:
|
||||
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
|
||||
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;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev)
|
||||
dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val);
|
||||
|
||||
if (s)
|
||||
seq_printf(s, "SMU idlemask : 0x%x\n", val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table)
|
||||
{
|
||||
if (!pdev->smu_virt_addr) {
|
||||
@ -403,6 +375,9 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
|
||||
int rc;
|
||||
u32 val;
|
||||
|
||||
if (dev->cpu_id == AMD_CPU_ID_PCO)
|
||||
return -ENODEV;
|
||||
|
||||
rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -449,12 +424,31 @@ static ssize_t smu_program_show(struct device *d, struct device_attribute *attr,
|
||||
static DEVICE_ATTR_RO(smu_fw_version);
|
||||
static DEVICE_ATTR_RO(smu_program);
|
||||
|
||||
static umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
|
||||
|
||||
if (pdev->cpu_id == AMD_CPU_ID_PCO)
|
||||
return 0;
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static struct attribute *pmc_attrs[] = {
|
||||
&dev_attr_smu_fw_version.attr,
|
||||
&dev_attr_smu_program.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(pmc);
|
||||
|
||||
static struct attribute_group pmc_attr_group = {
|
||||
.attrs = pmc_attrs,
|
||||
.is_visible = pmc_attr_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *pmc_groups[] = {
|
||||
&pmc_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int smu_fw_info_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
@ -521,28 +515,47 @@ static int s0ix_stats_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(s0ix_stats);
|
||||
|
||||
static int amd_pmc_idlemask_show(struct seq_file *s, void *unused)
|
||||
static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
|
||||
struct seq_file *s)
|
||||
{
|
||||
struct amd_pmc_dev *dev = s->private;
|
||||
u32 val;
|
||||
int rc;
|
||||
|
||||
/* we haven't yet read SMU version */
|
||||
if (!dev->major) {
|
||||
rc = amd_pmc_get_smu_version(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
switch (pdev->cpu_id) {
|
||||
case AMD_CPU_ID_CZN:
|
||||
/* we haven't yet read SMU version */
|
||||
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
|
||||
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;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) {
|
||||
rc = amd_pmc_idlemask_read(dev, NULL, s);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
seq_puts(s, "Unsupported SMU version for Idlemask\n");
|
||||
}
|
||||
if (dev)
|
||||
dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val);
|
||||
|
||||
if (s)
|
||||
seq_printf(s, "SMU idlemask : 0x%x\n", val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmc_idlemask_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return amd_pmc_idlemask_read(s->private, NULL, s);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask);
|
||||
|
||||
static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
|
||||
@ -812,6 +825,14 @@ static void amd_pmc_s2idle_check(void)
|
||||
dev_err(pdev->dev, "error writing to STB: %d\n", rc);
|
||||
}
|
||||
|
||||
static int amd_pmc_dump_data(struct amd_pmc_dev *pdev)
|
||||
{
|
||||
if (pdev->cpu_id == AMD_CPU_ID_PCO)
|
||||
return -ENODEV;
|
||||
|
||||
return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0);
|
||||
}
|
||||
|
||||
static void amd_pmc_s2idle_restore(void)
|
||||
{
|
||||
struct amd_pmc_dev *pdev = &pmc;
|
||||
@ -824,7 +845,7 @@ static void amd_pmc_s2idle_restore(void)
|
||||
dev_err(pdev->dev, "resume failed: %d\n", rc);
|
||||
|
||||
/* Let SMU know that we are looking for stats */
|
||||
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0);
|
||||
amd_pmc_dump_data(pdev);
|
||||
|
||||
rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE);
|
||||
if (rc)
|
||||
@ -840,7 +861,7 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
|
||||
.restore = amd_pmc_s2idle_restore,
|
||||
};
|
||||
|
||||
static int __maybe_unused amd_pmc_suspend_handler(struct device *dev)
|
||||
static int amd_pmc_suspend_handler(struct device *dev)
|
||||
{
|
||||
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
|
||||
|
||||
@ -866,6 +887,7 @@ static const struct pci_device_id pmc_pci_ids[] = {
|
||||
{ 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) },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -902,17 +924,9 @@ static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0);
|
||||
err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "failed to write addr in stb: 0x%X\n",
|
||||
AMD_PMC_STB_INDEX_ADDRESS);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
|
||||
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, data);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "failed to write data in stb: 0x%X\n",
|
||||
AMD_PMC_STB_INDEX_DATA);
|
||||
dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
|
||||
@ -923,18 +937,10 @@ static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error writing addr to stb: 0x%X\n",
|
||||
AMD_PMC_STB_INDEX_ADDRESS);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
|
||||
for (i = 0; i < FIFO_SIZE; i++) {
|
||||
err = pci_read_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, buf++);
|
||||
err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error reading data from stb: 0x%X\n",
|
||||
AMD_PMC_STB_INDEX_DATA);
|
||||
dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
}
|
||||
@ -960,31 +966,26 @@ static int amd_pmc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dev->cpu_id = rdev->device;
|
||||
dev->rdev = rdev;
|
||||
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
|
||||
err = pcibios_err_to_errno(err);
|
||||
|
||||
if (dev->cpu_id == AMD_CPU_ID_SP) {
|
||||
dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
|
||||
err = -ENODEV;
|
||||
goto err_pci_dev_put;
|
||||
}
|
||||
|
||||
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
|
||||
dev->rdev = rdev;
|
||||
err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO);
|
||||
err = pcibios_err_to_errno(err);
|
||||
goto err_pci_dev_put;
|
||||
}
|
||||
|
||||
base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
|
||||
|
||||
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
|
||||
err = pcibios_err_to_errno(err);
|
||||
goto err_pci_dev_put;
|
||||
}
|
||||
|
||||
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
|
||||
err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI);
|
||||
err = pcibios_err_to_errno(err);
|
||||
goto err_pci_dev_put;
|
||||
}
|
||||
@ -1022,7 +1023,7 @@ err_pci_dev_put:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int amd_pmc_remove(struct platform_device *pdev)
|
||||
static void amd_pmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1031,7 +1032,6 @@ static int amd_pmc_remove(struct platform_device *pdev)
|
||||
amd_pmc_dbgfs_unregister(dev);
|
||||
pci_dev_put(dev->rdev);
|
||||
mutex_destroy(&dev->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id amd_pmc_acpi_ids[] = {
|
||||
@ -1054,7 +1054,7 @@ static struct platform_driver amd_pmc_driver = {
|
||||
.pm = pm_sleep_ptr(&amd_pmc_pm),
|
||||
},
|
||||
.probe = amd_pmc_probe,
|
||||
.remove = amd_pmc_remove,
|
||||
.remove_new = amd_pmc_remove,
|
||||
};
|
||||
module_platform_driver(amd_pmc_driver);
|
||||
|
||||
|
@ -7,6 +7,7 @@ config AMD_PMF
|
||||
tristate "AMD Platform Management Framework"
|
||||
depends on ACPI && PCI
|
||||
depends on POWER_SUPPLY
|
||||
depends on AMD_NB
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
help
|
||||
This driver provides support for the AMD Platform Management Framework.
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
*/
|
||||
|
||||
#include <asm/amd_nb.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
@ -22,8 +23,6 @@
|
||||
#define AMD_PMF_REGISTER_ARGUMENT 0xA58
|
||||
|
||||
/* Base address of SMU for mapping physical address to virtual address */
|
||||
#define AMD_PMF_SMU_INDEX_ADDRESS 0xB8
|
||||
#define AMD_PMF_SMU_INDEX_DATA 0xBC
|
||||
#define AMD_PMF_MAPPING_SIZE 0x01000
|
||||
#define AMD_PMF_BASE_ADDR_OFFSET 0x10000
|
||||
#define AMD_PMF_BASE_ADDR_LO 0x13B102E8
|
||||
@ -348,30 +347,19 @@ static int amd_pmf_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dev->cpu_id = rdev->device;
|
||||
err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
|
||||
pci_dev_put(rdev);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
|
||||
err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
|
||||
err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO);
|
||||
pci_dev_put(rdev);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
|
||||
base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK;
|
||||
|
||||
err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
|
||||
pci_dev_put(rdev);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
|
||||
err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
|
||||
err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI);
|
||||
pci_dev_put(rdev);
|
||||
return pcibios_err_to_errno(err);
|
||||
}
|
||||
@ -402,7 +390,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_remove(struct platform_device *pdev)
|
||||
static void amd_pmf_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
|
||||
|
||||
@ -413,7 +401,6 @@ static int amd_pmf_remove(struct platform_device *pdev)
|
||||
mutex_destroy(&dev->lock);
|
||||
mutex_destroy(&dev->update_mutex);
|
||||
kfree(dev->buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct attribute_group *amd_pmf_driver_groups[] = {
|
||||
@ -428,7 +415,7 @@ static struct platform_driver amd_pmf_driver = {
|
||||
.dev_groups = amd_pmf_driver_groups,
|
||||
},
|
||||
.probe = amd_pmf_probe,
|
||||
.remove = amd_pmf_remove,
|
||||
.remove_new = amd_pmf_remove,
|
||||
};
|
||||
module_platform_driver(amd_pmf_driver);
|
||||
|
||||
|
@ -124,11 +124,10 @@ fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int amilo_rfkill_remove(struct platform_device *device)
|
||||
static void amilo_rfkill_remove(struct platform_device *device)
|
||||
{
|
||||
rfkill_unregister(amilo_rfkill_dev);
|
||||
rfkill_destroy(amilo_rfkill_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver amilo_rfkill_driver = {
|
||||
@ -136,7 +135,7 @@ static struct platform_driver amilo_rfkill_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = amilo_rfkill_probe,
|
||||
.remove = amilo_rfkill_remove,
|
||||
.remove_new = amilo_rfkill_remove,
|
||||
};
|
||||
|
||||
static int __init amilo_rfkill_init(void)
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
|
||||
* Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
|
||||
* Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
|
||||
* Copyright (C) 2023 Orlando Chamberlain <orlandoch.dev@gmail.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@ -15,38 +16,51 @@
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pnp.h>
|
||||
#include <linux/apple_bl.h>
|
||||
#include <linux/apple-gmux.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vga_switcheroo.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <acpi/video.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/**
|
||||
* DOC: Overview
|
||||
*
|
||||
* gmux is a microcontroller built into the MacBook Pro to support dual GPUs:
|
||||
* A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on retinas.
|
||||
* A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on pre-T2 retinas.
|
||||
*
|
||||
* On T2 Macbooks, the gmux is part of the T2 Coprocessor's SMC. The SMC has
|
||||
* an I2C connection to a `NXP PCAL6524` GPIO expander, which enables/disables
|
||||
* the voltage regulators of the discrete GPU, drives the display panel power,
|
||||
* and has a GPIO to switch the eDP mux. The Intel CPU can interact with
|
||||
* gmux through MMIO, similar to how the main SMC interface is controlled.
|
||||
*
|
||||
* (The MacPro6,1 2013 also has a gmux, however it is unclear why since it has
|
||||
* dual GPUs but no built-in display.)
|
||||
*
|
||||
* gmux is connected to the LPC bus of the southbridge. Its I/O ports are
|
||||
* accessed differently depending on the microcontroller: Driver functions
|
||||
* to access a pre-retina gmux are infixed ``_pio_``, those for a retina gmux
|
||||
* are infixed ``_index_``.
|
||||
* to access a pre-retina gmux are infixed ``_pio_``, those for a pre-T2
|
||||
* retina gmux are infixed ``_index_``, and those on T2 Macs are infixed
|
||||
* with ``_mmio_``.
|
||||
*
|
||||
* .. _Lattice XP2:
|
||||
* http://www.latticesemi.com/en/Products/FPGAandCPLD/LatticeXP2.aspx
|
||||
* .. _Renesas R4F2113:
|
||||
* http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp
|
||||
* .. _NXP PCAL6524:
|
||||
* https://www.nxp.com/docs/en/data-sheet/PCAL6524.pdf
|
||||
*/
|
||||
|
||||
struct apple_gmux_config;
|
||||
|
||||
struct apple_gmux_data {
|
||||
u8 __iomem *iomem_base;
|
||||
unsigned long iostart;
|
||||
unsigned long iolen;
|
||||
bool indexed;
|
||||
const struct apple_gmux_config *config;
|
||||
struct mutex index_lock;
|
||||
|
||||
struct backlight_device *bdev;
|
||||
@ -60,10 +74,26 @@ struct apple_gmux_data {
|
||||
enum vga_switcheroo_client_id switch_state_external;
|
||||
enum vga_switcheroo_state power_state;
|
||||
struct completion powerchange_done;
|
||||
|
||||
/* debugfs data */
|
||||
u8 selected_port;
|
||||
struct dentry *debug_dentry;
|
||||
};
|
||||
|
||||
static struct apple_gmux_data *apple_gmux_data;
|
||||
|
||||
struct apple_gmux_config {
|
||||
u8 (*read8)(struct apple_gmux_data *gmux_data, int port);
|
||||
void (*write8)(struct apple_gmux_data *gmux_data, int port, u8 val);
|
||||
u32 (*read32)(struct apple_gmux_data *gmux_data, int port);
|
||||
void (*write32)(struct apple_gmux_data *gmux_data, int port, u32 val);
|
||||
const struct vga_switcheroo_handler *gmux_handler;
|
||||
enum vga_switcheroo_handler_flags_t handler_flags;
|
||||
unsigned long resource_type;
|
||||
bool read_version_as_u32;
|
||||
char *name;
|
||||
};
|
||||
|
||||
#define GMUX_INTERRUPT_ENABLE 0xff
|
||||
#define GMUX_INTERRUPT_DISABLE 0x00
|
||||
|
||||
@ -193,37 +223,98 @@ static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
|
||||
mutex_unlock(&gmux_data->index_lock);
|
||||
}
|
||||
|
||||
static int gmux_mmio_wait(struct apple_gmux_data *gmux_data)
|
||||
{
|
||||
int i = 200;
|
||||
u8 gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
|
||||
while (i && gwr) {
|
||||
gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
udelay(100);
|
||||
i--;
|
||||
}
|
||||
|
||||
return !!i;
|
||||
}
|
||||
|
||||
static u8 gmux_mmio_read8(struct apple_gmux_data *gmux_data, int port)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&gmux_data->index_lock);
|
||||
gmux_mmio_wait(gmux_data);
|
||||
iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
|
||||
iowrite8(GMUX_MMIO_READ | sizeof(val),
|
||||
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
gmux_mmio_wait(gmux_data);
|
||||
val = ioread8(gmux_data->iomem_base);
|
||||
mutex_unlock(&gmux_data->index_lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void gmux_mmio_write8(struct apple_gmux_data *gmux_data, int port,
|
||||
u8 val)
|
||||
{
|
||||
mutex_lock(&gmux_data->index_lock);
|
||||
gmux_mmio_wait(gmux_data);
|
||||
iowrite8(val, gmux_data->iomem_base);
|
||||
|
||||
iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
|
||||
iowrite8(GMUX_MMIO_WRITE | sizeof(val),
|
||||
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
|
||||
gmux_mmio_wait(gmux_data);
|
||||
mutex_unlock(&gmux_data->index_lock);
|
||||
}
|
||||
|
||||
static u32 gmux_mmio_read32(struct apple_gmux_data *gmux_data, int port)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
mutex_lock(&gmux_data->index_lock);
|
||||
gmux_mmio_wait(gmux_data);
|
||||
iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
|
||||
iowrite8(GMUX_MMIO_READ | sizeof(val),
|
||||
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
gmux_mmio_wait(gmux_data);
|
||||
val = be32_to_cpu(ioread32(gmux_data->iomem_base));
|
||||
mutex_unlock(&gmux_data->index_lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void gmux_mmio_write32(struct apple_gmux_data *gmux_data, int port,
|
||||
u32 val)
|
||||
{
|
||||
mutex_lock(&gmux_data->index_lock);
|
||||
iowrite32(cpu_to_be32(val), gmux_data->iomem_base);
|
||||
iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
|
||||
iowrite8(GMUX_MMIO_WRITE | sizeof(val),
|
||||
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
gmux_mmio_wait(gmux_data);
|
||||
mutex_unlock(&gmux_data->index_lock);
|
||||
}
|
||||
|
||||
static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
|
||||
{
|
||||
if (gmux_data->indexed)
|
||||
return gmux_index_read8(gmux_data, port);
|
||||
else
|
||||
return gmux_pio_read8(gmux_data, port);
|
||||
return gmux_data->config->read8(gmux_data, port);
|
||||
}
|
||||
|
||||
static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
|
||||
{
|
||||
if (gmux_data->indexed)
|
||||
gmux_index_write8(gmux_data, port, val);
|
||||
else
|
||||
gmux_pio_write8(gmux_data, port, val);
|
||||
return gmux_data->config->write8(gmux_data, port, val);
|
||||
}
|
||||
|
||||
static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
|
||||
{
|
||||
if (gmux_data->indexed)
|
||||
return gmux_index_read32(gmux_data, port);
|
||||
else
|
||||
return gmux_pio_read32(gmux_data, port);
|
||||
return gmux_data->config->read32(gmux_data, port);
|
||||
}
|
||||
|
||||
static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
|
||||
u32 val)
|
||||
{
|
||||
if (gmux_data->indexed)
|
||||
gmux_index_write32(gmux_data, port, val);
|
||||
else
|
||||
gmux_pio_write32(gmux_data, port, val);
|
||||
return gmux_data->config->write32(gmux_data, port, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,8 +324,8 @@ static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
|
||||
* the GPU. On dual GPU MacBook Pros by contrast, either GPU may be suspended
|
||||
* to conserve energy. Hence the PWM signal needs to be generated by a separate
|
||||
* backlight driver which is controlled by gmux. The earliest generation
|
||||
* MBP5 2008/09 uses a `TI LP8543`_ backlight driver. All newer models
|
||||
* use a `TI LP8545`_.
|
||||
* MBP5 2008/09 uses a `TI LP8543`_ backlight driver. Newer models
|
||||
* use a `TI LP8545`_ or a TI LP8548.
|
||||
*
|
||||
* .. _TI LP8543: https://www.ti.com/lit/ds/symlink/lp8543.pdf
|
||||
* .. _TI LP8545: https://www.ti.com/lit/ds/symlink/lp8545.pdf
|
||||
@ -298,8 +389,8 @@ static const struct backlight_ops gmux_bl_ops = {
|
||||
* connecting it either to the discrete GPU or the Thunderbolt controller.
|
||||
* Oddly enough, while the full port is no longer switchable, AUX and HPD
|
||||
* are still switchable by way of an `NXP CBTL03062`_ (on pre-retinas
|
||||
* MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on retinas) under the
|
||||
* control of gmux. Since the integrated GPU is missing the main link,
|
||||
* MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on pre-t2 retinas) under
|
||||
* the control of gmux. Since the integrated GPU is missing the main link,
|
||||
* external displays appear to it as phantoms which fail to link-train.
|
||||
*
|
||||
* gmux receives the HPD signal of all display connectors and sends an
|
||||
@ -346,10 +437,10 @@ static void gmux_read_switch_state(struct apple_gmux_data *gmux_data)
|
||||
else
|
||||
gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS;
|
||||
|
||||
if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
|
||||
gmux_data->switch_state_display = VGA_SWITCHEROO_IGD;
|
||||
else
|
||||
if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) & 1)
|
||||
gmux_data->switch_state_display = VGA_SWITCHEROO_DIS;
|
||||
else
|
||||
gmux_data->switch_state_display = VGA_SWITCHEROO_IGD;
|
||||
|
||||
if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2)
|
||||
gmux_data->switch_state_external = VGA_SWITCHEROO_IGD;
|
||||
@ -463,27 +554,79 @@ static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev)
|
||||
return VGA_SWITCHEROO_DIS;
|
||||
}
|
||||
|
||||
static const struct vga_switcheroo_handler gmux_handler_indexed = {
|
||||
static const struct vga_switcheroo_handler gmux_handler_no_ddc = {
|
||||
.switchto = gmux_switchto,
|
||||
.power_state = gmux_set_power_state,
|
||||
.get_client_id = gmux_get_client_id,
|
||||
};
|
||||
|
||||
static const struct vga_switcheroo_handler gmux_handler_classic = {
|
||||
static const struct vga_switcheroo_handler gmux_handler_ddc = {
|
||||
.switchto = gmux_switchto,
|
||||
.switch_ddc = gmux_switch_ddc,
|
||||
.power_state = gmux_set_power_state,
|
||||
.get_client_id = gmux_get_client_id,
|
||||
};
|
||||
|
||||
static const struct apple_gmux_config apple_gmux_pio = {
|
||||
.read8 = &gmux_pio_read8,
|
||||
.write8 = &gmux_pio_write8,
|
||||
.read32 = &gmux_pio_read32,
|
||||
.write32 = &gmux_pio_write32,
|
||||
.gmux_handler = &gmux_handler_ddc,
|
||||
.handler_flags = VGA_SWITCHEROO_CAN_SWITCH_DDC,
|
||||
.resource_type = IORESOURCE_IO,
|
||||
.read_version_as_u32 = false,
|
||||
.name = "classic"
|
||||
};
|
||||
|
||||
static const struct apple_gmux_config apple_gmux_index = {
|
||||
.read8 = &gmux_index_read8,
|
||||
.write8 = &gmux_index_write8,
|
||||
.read32 = &gmux_index_read32,
|
||||
.write32 = &gmux_index_write32,
|
||||
.gmux_handler = &gmux_handler_no_ddc,
|
||||
.handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG,
|
||||
.resource_type = IORESOURCE_IO,
|
||||
.read_version_as_u32 = true,
|
||||
.name = "indexed"
|
||||
};
|
||||
|
||||
static const struct apple_gmux_config apple_gmux_mmio = {
|
||||
.read8 = &gmux_mmio_read8,
|
||||
.write8 = &gmux_mmio_write8,
|
||||
.read32 = &gmux_mmio_read32,
|
||||
.write32 = &gmux_mmio_write32,
|
||||
.gmux_handler = &gmux_handler_no_ddc,
|
||||
.handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG,
|
||||
.resource_type = IORESOURCE_MEM,
|
||||
.read_version_as_u32 = true,
|
||||
.name = "T2"
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* DOC: Interrupt
|
||||
*
|
||||
* gmux is also connected to a GPIO pin of the southbridge and thereby is able
|
||||
* to trigger an ACPI GPE. On the MBP5 2008/09 it's GPIO pin 22 of the Nvidia
|
||||
* MCP79, on all following generations it's GPIO pin 6 of the Intel PCH.
|
||||
* to trigger an ACPI GPE. ACPI name GMGP holds this GPIO pin's number. On the
|
||||
* MBP5 2008/09 it's GPIO pin 22 of the Nvidia MCP79, on following generations
|
||||
* it's GPIO pin 6 of the Intel PCH, on MMIO gmux's it's pin 21.
|
||||
*
|
||||
* The GPE merely signals that an interrupt occurred, the actual type of event
|
||||
* is identified by reading a gmux register.
|
||||
*
|
||||
* In addition to the GMGP name, gmux's ACPI device also has two methods GMSP
|
||||
* and GMLV. GMLV likely means "GMUX Level", and reads the value of the GPIO,
|
||||
* while GMSP likely means "GMUX Set Polarity", and seems to write to the GPIO's
|
||||
* value. On newer Macbooks (This was introduced with or sometime before the
|
||||
* MacBookPro14,3), the ACPI GPE method differentiates between the OS type: On
|
||||
* Darwin, only a notification is signaled, whereas on other OSes, the GPIO's
|
||||
* value is read and then inverted.
|
||||
*
|
||||
* Because Linux masquerades as Darwin, it ends up in the notification-only code
|
||||
* path. On MMIO gmux's, this seems to lead to us being unable to clear interrupts,
|
||||
* unless we call GMSP(0). Without this, there is a flood of status=0 interrupts
|
||||
* that can't be cleared. This issue seems to be unique to MMIO gmux's.
|
||||
*/
|
||||
|
||||
static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
|
||||
@ -510,6 +653,9 @@ static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
|
||||
/* to clear interrupts write back current status */
|
||||
status = gmux_interrupt_get_status(gmux_data);
|
||||
gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
|
||||
/* Prevent flood of status=0 interrupts */
|
||||
if (gmux_data->config == &apple_gmux_mmio)
|
||||
acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
|
||||
}
|
||||
|
||||
static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
|
||||
@ -529,6 +675,80 @@ static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
|
||||
complete(&gmux_data->powerchange_done);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: Debugfs Interface
|
||||
*
|
||||
* gmux ports can be accessed from userspace as a debugfs interface. For example:
|
||||
*
|
||||
* # echo 4 > /sys/kernel/debug/apple_gmux/selected_port
|
||||
* # cat /sys/kernel/debug/apple_gmux/selected_port_data | xxd -p
|
||||
* 00000005
|
||||
*
|
||||
* Reads 4 bytes from port 4 (GMUX_PORT_VERSION_MAJOR).
|
||||
*
|
||||
* 1 and 4 byte writes are also allowed.
|
||||
*/
|
||||
|
||||
static ssize_t gmux_selected_port_data_write(struct file *file,
|
||||
const char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct apple_gmux_data *gmux_data = file->private_data;
|
||||
|
||||
if (*ppos)
|
||||
return -EINVAL;
|
||||
|
||||
if (count == 1) {
|
||||
u8 data;
|
||||
|
||||
if (copy_from_user(&data, userbuf, 1))
|
||||
return -EFAULT;
|
||||
|
||||
gmux_write8(gmux_data, gmux_data->selected_port, data);
|
||||
} else if (count == 4) {
|
||||
u32 data;
|
||||
|
||||
if (copy_from_user(&data, userbuf, 4))
|
||||
return -EFAULT;
|
||||
|
||||
gmux_write32(gmux_data, gmux_data->selected_port, data);
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t gmux_selected_port_data_read(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct apple_gmux_data *gmux_data = file->private_data;
|
||||
u32 data;
|
||||
|
||||
data = gmux_read32(gmux_data, gmux_data->selected_port);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, &data, sizeof(data));
|
||||
}
|
||||
|
||||
static const struct file_operations gmux_port_data_ops = {
|
||||
.open = simple_open,
|
||||
.write = gmux_selected_port_data_write,
|
||||
.read = gmux_selected_port_data_read
|
||||
};
|
||||
|
||||
static void gmux_init_debugfs(struct apple_gmux_data *gmux_data)
|
||||
{
|
||||
gmux_data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
|
||||
debugfs_create_u8("selected_port", 0644, gmux_data->debug_dentry,
|
||||
&gmux_data->selected_port);
|
||||
debugfs_create_file("selected_port_data", 0644, gmux_data->debug_dentry,
|
||||
gmux_data, &gmux_port_data_ops);
|
||||
}
|
||||
|
||||
static void gmux_fini_debugfs(struct apple_gmux_data *gmux_data)
|
||||
{
|
||||
debugfs_remove_recursive(gmux_data->debug_dentry);
|
||||
}
|
||||
|
||||
static int gmux_suspend(struct device *dev)
|
||||
{
|
||||
struct pnp_dev *pnp = to_pnp_dev(dev);
|
||||
@ -560,18 +780,19 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
struct apple_gmux_data *gmux_data;
|
||||
struct resource *res;
|
||||
struct backlight_properties props;
|
||||
struct backlight_device *bdev;
|
||||
struct backlight_device *bdev = NULL;
|
||||
u8 ver_major, ver_minor, ver_release;
|
||||
bool register_bdev = true;
|
||||
int ret = -ENXIO;
|
||||
acpi_status status;
|
||||
unsigned long long gpe;
|
||||
bool indexed = false;
|
||||
enum apple_gmux_type type;
|
||||
u32 version;
|
||||
|
||||
if (apple_gmux_data)
|
||||
return -EBUSY;
|
||||
|
||||
if (!apple_gmux_detect(pnp, &indexed)) {
|
||||
if (!apple_gmux_detect(pnp, &type)) {
|
||||
pr_info("gmux device not present\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -581,6 +802,35 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
return -ENOMEM;
|
||||
pnp_set_drvdata(pnp, gmux_data);
|
||||
|
||||
switch (type) {
|
||||
case APPLE_GMUX_TYPE_MMIO:
|
||||
gmux_data->config = &apple_gmux_mmio;
|
||||
mutex_init(&gmux_data->index_lock);
|
||||
|
||||
res = pnp_get_resource(pnp, IORESOURCE_MEM, 0);
|
||||
gmux_data->iostart = res->start;
|
||||
/* Although the ACPI table only allocates 8 bytes, we need 16. */
|
||||
gmux_data->iolen = 16;
|
||||
if (!request_mem_region(gmux_data->iostart, gmux_data->iolen,
|
||||
"Apple gmux")) {
|
||||
pr_err("gmux I/O already in use\n");
|
||||
goto err_free;
|
||||
}
|
||||
gmux_data->iomem_base = ioremap(gmux_data->iostart, gmux_data->iolen);
|
||||
if (!gmux_data->iomem_base) {
|
||||
pr_err("couldn't remap gmux mmio region");
|
||||
goto err_release;
|
||||
}
|
||||
goto get_version;
|
||||
case APPLE_GMUX_TYPE_INDEXED:
|
||||
gmux_data->config = &apple_gmux_index;
|
||||
mutex_init(&gmux_data->index_lock);
|
||||
break;
|
||||
case APPLE_GMUX_TYPE_PIO:
|
||||
gmux_data->config = &apple_gmux_pio;
|
||||
break;
|
||||
}
|
||||
|
||||
res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
|
||||
gmux_data->iostart = res->start;
|
||||
gmux_data->iolen = resource_size(res);
|
||||
@ -591,9 +841,8 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (indexed) {
|
||||
mutex_init(&gmux_data->index_lock);
|
||||
gmux_data->indexed = true;
|
||||
get_version:
|
||||
if (gmux_data->config->read_version_as_u32) {
|
||||
version = gmux_read32(gmux_data, GMUX_PORT_VERSION_MAJOR);
|
||||
ver_major = (version >> 24) & 0xff;
|
||||
ver_minor = (version >> 16) & 0xff;
|
||||
@ -604,40 +853,37 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
|
||||
}
|
||||
pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
|
||||
ver_release, (gmux_data->indexed ? "indexed" : "classic"));
|
||||
ver_release, gmux_data->config->name);
|
||||
|
||||
memset(&props, 0, sizeof(props));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
|
||||
|
||||
/*
|
||||
* Currently it's assumed that the maximum brightness is less than
|
||||
* 2^24 for compatibility with old gmux versions. Cap the max
|
||||
* brightness at this value, but print a warning if the hardware
|
||||
* reports something higher so that it can be fixed.
|
||||
*/
|
||||
if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
|
||||
props.max_brightness = GMUX_MAX_BRIGHTNESS;
|
||||
#if IS_REACHABLE(CONFIG_ACPI_VIDEO)
|
||||
register_bdev = acpi_video_get_backlight_type() == acpi_backlight_apple_gmux;
|
||||
#endif
|
||||
if (register_bdev) {
|
||||
/*
|
||||
* Currently it's assumed that the maximum brightness is less than
|
||||
* 2^24 for compatibility with old gmux versions. Cap the max
|
||||
* brightness at this value, but print a warning if the hardware
|
||||
* reports something higher so that it can be fixed.
|
||||
*/
|
||||
if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
|
||||
props.max_brightness = GMUX_MAX_BRIGHTNESS;
|
||||
|
||||
bdev = backlight_device_register("gmux_backlight", &pnp->dev,
|
||||
gmux_data, &gmux_bl_ops, &props);
|
||||
if (IS_ERR(bdev)) {
|
||||
ret = PTR_ERR(bdev);
|
||||
goto err_release;
|
||||
bdev = backlight_device_register("gmux_backlight", &pnp->dev,
|
||||
gmux_data, &gmux_bl_ops, &props);
|
||||
if (IS_ERR(bdev)) {
|
||||
ret = PTR_ERR(bdev);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
gmux_data->bdev = bdev;
|
||||
bdev->props.brightness = gmux_get_brightness(bdev);
|
||||
backlight_update_status(bdev);
|
||||
}
|
||||
|
||||
gmux_data->bdev = bdev;
|
||||
bdev->props.brightness = gmux_get_brightness(bdev);
|
||||
backlight_update_status(bdev);
|
||||
|
||||
/*
|
||||
* The backlight situation on Macs is complicated. If the gmux is
|
||||
* present it's the best choice, because it always works for
|
||||
* backlight control and supports more levels than other options.
|
||||
* Disable the other backlight choices.
|
||||
*/
|
||||
apple_bl_unregister();
|
||||
|
||||
gmux_data->power_state = VGA_SWITCHEROO_ON;
|
||||
|
||||
gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
|
||||
@ -690,21 +936,18 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
/*
|
||||
* Retina MacBook Pros cannot switch the panel's AUX separately
|
||||
* and need eDP pre-calibration. They are distinguishable from
|
||||
* pre-retinas by having an "indexed" gmux.
|
||||
* pre-retinas by having an "indexed" or "T2" gmux.
|
||||
*
|
||||
* Pre-retina MacBook Pros can switch the panel's DDC separately.
|
||||
*/
|
||||
if (gmux_data->indexed)
|
||||
ret = vga_switcheroo_register_handler(&gmux_handler_indexed,
|
||||
VGA_SWITCHEROO_NEEDS_EDP_CONFIG);
|
||||
else
|
||||
ret = vga_switcheroo_register_handler(&gmux_handler_classic,
|
||||
VGA_SWITCHEROO_CAN_SWITCH_DDC);
|
||||
ret = vga_switcheroo_register_handler(gmux_data->config->gmux_handler,
|
||||
gmux_data->config->handler_flags);
|
||||
if (ret) {
|
||||
pr_err("Failed to register vga_switcheroo handler\n");
|
||||
goto err_register_handler;
|
||||
}
|
||||
|
||||
gmux_init_debugfs(gmux_data);
|
||||
return 0;
|
||||
|
||||
err_register_handler:
|
||||
@ -719,8 +962,14 @@ err_enable_gpe:
|
||||
&gmux_notify_handler);
|
||||
err_notify:
|
||||
backlight_device_unregister(bdev);
|
||||
err_unmap:
|
||||
if (gmux_data->iomem_base)
|
||||
iounmap(gmux_data->iomem_base);
|
||||
err_release:
|
||||
release_region(gmux_data->iostart, gmux_data->iolen);
|
||||
if (gmux_data->config->resource_type == IORESOURCE_MEM)
|
||||
release_mem_region(gmux_data->iostart, gmux_data->iolen);
|
||||
else
|
||||
release_region(gmux_data->iostart, gmux_data->iolen);
|
||||
err_free:
|
||||
kfree(gmux_data);
|
||||
return ret;
|
||||
@ -730,6 +979,7 @@ static void gmux_remove(struct pnp_dev *pnp)
|
||||
{
|
||||
struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
|
||||
|
||||
gmux_fini_debugfs(gmux_data);
|
||||
vga_switcheroo_unregister_handler();
|
||||
gmux_disable_interrupts(gmux_data);
|
||||
if (gmux_data->gpe >= 0) {
|
||||
@ -741,11 +991,13 @@ static void gmux_remove(struct pnp_dev *pnp)
|
||||
|
||||
backlight_device_unregister(gmux_data->bdev);
|
||||
|
||||
release_region(gmux_data->iostart, gmux_data->iolen);
|
||||
if (gmux_data->iomem_base) {
|
||||
iounmap(gmux_data->iomem_base);
|
||||
release_mem_region(gmux_data->iostart, gmux_data->iolen);
|
||||
} else
|
||||
release_region(gmux_data->iostart, gmux_data->iolen);
|
||||
apple_gmux_data = NULL;
|
||||
kfree(gmux_data);
|
||||
|
||||
apple_bl_register();
|
||||
}
|
||||
|
||||
static const struct pnp_device_id gmux_device_ids[] = {
|
||||
|
@ -370,7 +370,7 @@ err_leds:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int p50_gpio_remove(struct platform_device *pdev)
|
||||
static void p50_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct p50_gpio *p50 = platform_get_drvdata(pdev);
|
||||
|
||||
@ -378,8 +378,6 @@ static int p50_gpio_remove(struct platform_device *pdev)
|
||||
platform_device_unregister(p50->leds_pdev);
|
||||
|
||||
gpiod_remove_lookup_table(&p50_gpio_led_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver p50_gpio_driver = {
|
||||
@ -387,7 +385,7 @@ static struct platform_driver p50_gpio_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = p50_gpio_probe,
|
||||
.remove = p50_gpio_remove,
|
||||
.remove_new = p50_gpio_remove,
|
||||
};
|
||||
|
||||
/* Board setup */
|
||||
|
@ -1134,7 +1134,7 @@ static void cmpc_exit(void)
|
||||
module_init(cmpc_init);
|
||||
module_exit(cmpc_exit);
|
||||
|
||||
static const struct acpi_device_id cmpc_device_ids[] = {
|
||||
static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
|
||||
{CMPC_ACCEL_HID, 0},
|
||||
{CMPC_ACCEL_HID_V4, 0},
|
||||
{CMPC_TABLET_HID, 0},
|
||||
|
@ -1003,12 +1003,12 @@ remove:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int compal_remove(struct platform_device *pdev)
|
||||
static void compal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct compal_data *data;
|
||||
|
||||
if (!extra_features)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
pr_info("Unloading: resetting fan control to motherboard\n");
|
||||
pwm_disable_control();
|
||||
@ -1017,8 +1017,6 @@ static int compal_remove(struct platform_device *pdev)
|
||||
power_supply_unregister(data->psy);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver compal_driver = {
|
||||
@ -1026,7 +1024,7 @@ static struct platform_driver compal_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = compal_probe,
|
||||
.remove = compal_remove,
|
||||
.remove_new = compal_remove,
|
||||
};
|
||||
|
||||
static int __init compal_init(void)
|
||||
|
@ -698,12 +698,10 @@ static int dcdbas_probe(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcdbas_remove(struct platform_device *dev)
|
||||
static void dcdbas_remove(struct platform_device *dev)
|
||||
{
|
||||
unregister_reboot_notifier(&dcdbas_reboot_nb);
|
||||
sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dcdbas_driver = {
|
||||
@ -711,7 +709,7 @@ static struct platform_driver dcdbas_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = dcdbas_probe,
|
||||
.remove = dcdbas_remove,
|
||||
.remove_new = dcdbas_remove,
|
||||
};
|
||||
|
||||
static const struct platform_device_info dcdbas_dev_info __initconst = {
|
||||
|
@ -97,6 +97,7 @@ static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *wwan_rfkill;
|
||||
static bool force_rfkill;
|
||||
static bool micmute_led_registered;
|
||||
static bool mute_led_registered;
|
||||
|
||||
module_param(force_rfkill, bool, 0444);
|
||||
MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
|
||||
@ -2177,6 +2178,34 @@ static struct led_classdev micmute_led_cdev = {
|
||||
.default_trigger = "audio-micmute",
|
||||
};
|
||||
|
||||
static int mute_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
struct calling_interface_token *token;
|
||||
int state = brightness != LED_OFF;
|
||||
|
||||
if (state == 0)
|
||||
token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE);
|
||||
else
|
||||
token = dell_smbios_find_token(GLOBAL_MUTE_ENABLE);
|
||||
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
dell_fill_request(&buffer, token->location, token->value, 0, 0);
|
||||
dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct led_classdev mute_led_cdev = {
|
||||
.name = "platform::mute",
|
||||
.max_brightness = 1,
|
||||
.brightness_set_blocking = mute_led_set,
|
||||
.default_trigger = "audio-mute",
|
||||
};
|
||||
|
||||
static int __init dell_init(void)
|
||||
{
|
||||
struct calling_interface_token *token;
|
||||
@ -2230,6 +2259,15 @@ static int __init dell_init(void)
|
||||
micmute_led_registered = true;
|
||||
}
|
||||
|
||||
if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) &&
|
||||
dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) {
|
||||
mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE);
|
||||
ret = led_classdev_register(&platform_device->dev, &mute_led_cdev);
|
||||
if (ret < 0)
|
||||
goto fail_backlight;
|
||||
mute_led_registered = true;
|
||||
}
|
||||
|
||||
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
|
||||
return 0;
|
||||
|
||||
@ -2277,6 +2315,8 @@ fail_get_brightness:
|
||||
fail_backlight:
|
||||
if (micmute_led_registered)
|
||||
led_classdev_unregister(&micmute_led_cdev);
|
||||
if (mute_led_registered)
|
||||
led_classdev_unregister(&mute_led_cdev);
|
||||
fail_led:
|
||||
dell_cleanup_rfkill();
|
||||
fail_rfkill:
|
||||
@ -2299,6 +2339,8 @@ static void __exit dell_exit(void)
|
||||
backlight_device_unregister(dell_backlight_device);
|
||||
if (micmute_led_registered)
|
||||
led_classdev_unregister(&micmute_led_cdev);
|
||||
if (mute_led_registered)
|
||||
led_classdev_unregister(&mute_led_cdev);
|
||||
dell_cleanup_rfkill();
|
||||
if (platform_device) {
|
||||
platform_device_unregister(platform_device);
|
||||
|
@ -34,6 +34,8 @@
|
||||
#define KBD_LED_AUTO_100_TOKEN 0x02F6
|
||||
#define GLOBAL_MIC_MUTE_ENABLE 0x0364
|
||||
#define GLOBAL_MIC_MUTE_DISABLE 0x0365
|
||||
#define GLOBAL_MUTE_ENABLE 0x058C
|
||||
#define GLOBAL_MUTE_DISABLE 0x058D
|
||||
|
||||
struct notifier_block;
|
||||
|
||||
|
@ -154,14 +154,13 @@ error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smo8800_remove(struct platform_device *device)
|
||||
static void smo8800_remove(struct platform_device *device)
|
||||
{
|
||||
struct smo8800_device *smo8800 = platform_get_drvdata(device);
|
||||
|
||||
free_irq(smo8800->irq, smo8800);
|
||||
misc_deregister(&smo8800->miscdev);
|
||||
dev_dbg(&device->dev, "device /dev/freefall unregistered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */
|
||||
@ -180,7 +179,7 @@ MODULE_DEVICE_TABLE(acpi, smo8800_ids);
|
||||
|
||||
static struct platform_driver smo8800_driver = {
|
||||
.probe = smo8800_probe,
|
||||
.remove = smo8800_remove,
|
||||
.remove_new = smo8800_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.acpi_match_table = smo8800_ids,
|
||||
|
@ -342,7 +342,7 @@ static int lis3lv02d_probe(struct platform_device *device)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lis3lv02d_remove(struct platform_device *device)
|
||||
static void lis3lv02d_remove(struct platform_device *device)
|
||||
{
|
||||
i8042_remove_filter(hp_accel_i8042_filter);
|
||||
lis3lv02d_joystick_disable(&lis3_dev);
|
||||
@ -352,7 +352,6 @@ static int lis3lv02d_remove(struct platform_device *device)
|
||||
flush_work(&hpled_led.work);
|
||||
|
||||
lis3lv02d_remove_fs(&lis3_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused lis3lv02d_suspend(struct device *dev)
|
||||
@ -373,7 +372,7 @@ static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
|
||||
/* For the HP MDPS aka 3D Driveguard */
|
||||
static struct platform_driver lis3lv02d_driver = {
|
||||
.probe = lis3lv02d_probe,
|
||||
.remove = lis3lv02d_remove,
|
||||
.remove_new = lis3lv02d_remove,
|
||||
.driver = {
|
||||
.name = "hp_accel",
|
||||
.pm = &hp_accel_pm,
|
||||
|
@ -170,11 +170,9 @@ static int __init tc1100_probe(struct platform_device *device)
|
||||
}
|
||||
|
||||
|
||||
static int tc1100_remove(struct platform_device *device)
|
||||
static void tc1100_remove(struct platform_device *device)
|
||||
{
|
||||
sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -223,7 +221,7 @@ static struct platform_driver tc1100_driver = {
|
||||
.pm = &tc1100_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.remove = tc1100_remove,
|
||||
.remove_new = tc1100_remove,
|
||||
};
|
||||
|
||||
static int __init tc1100_init(void)
|
||||
|
@ -830,7 +830,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int huawei_wmi_remove(struct platform_device *pdev)
|
||||
static void huawei_wmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
const struct wmi_device_id *guid = huawei_wmi_events_id_table;
|
||||
|
||||
@ -846,8 +846,6 @@ static int huawei_wmi_remove(struct platform_device *pdev)
|
||||
huawei_wmi_battery_exit(&pdev->dev);
|
||||
huawei_wmi_fn_lock_exit(&pdev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver huawei_wmi_driver = {
|
||||
@ -855,7 +853,7 @@ static struct platform_driver huawei_wmi_driver = {
|
||||
.name = "huawei-wmi",
|
||||
},
|
||||
.probe = huawei_wmi_probe,
|
||||
.remove = huawei_wmi_remove,
|
||||
.remove_new = huawei_wmi_remove,
|
||||
};
|
||||
|
||||
static __init int huawei_wmi_init(void)
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
@ -31,6 +30,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wmi.h>
|
||||
#include "ideapad-laptop.h"
|
||||
|
||||
#include <acpi/video.h>
|
||||
|
||||
@ -85,33 +85,6 @@ enum {
|
||||
SALS_FNLOCK_OFF = 0xf,
|
||||
};
|
||||
|
||||
enum {
|
||||
VPCCMD_R_VPC1 = 0x10,
|
||||
VPCCMD_R_BL_MAX,
|
||||
VPCCMD_R_BL,
|
||||
VPCCMD_W_BL,
|
||||
VPCCMD_R_WIFI,
|
||||
VPCCMD_W_WIFI,
|
||||
VPCCMD_R_BT,
|
||||
VPCCMD_W_BT,
|
||||
VPCCMD_R_BL_POWER,
|
||||
VPCCMD_R_NOVO,
|
||||
VPCCMD_R_VPC2,
|
||||
VPCCMD_R_TOUCHPAD,
|
||||
VPCCMD_W_TOUCHPAD,
|
||||
VPCCMD_R_CAMERA,
|
||||
VPCCMD_W_CAMERA,
|
||||
VPCCMD_R_3G,
|
||||
VPCCMD_W_3G,
|
||||
VPCCMD_R_ODD, /* 0x21 */
|
||||
VPCCMD_W_FAN,
|
||||
VPCCMD_R_RF,
|
||||
VPCCMD_W_RF,
|
||||
VPCCMD_R_FAN = 0x2B,
|
||||
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
|
||||
VPCCMD_W_BL_POWER = 0x33,
|
||||
};
|
||||
|
||||
struct ideapad_dytc_priv {
|
||||
enum platform_profile_option current_profile;
|
||||
struct platform_profile_handler pprof;
|
||||
@ -227,7 +200,6 @@ static void ideapad_shared_exit(struct ideapad_private *priv)
|
||||
/*
|
||||
* ACPI Helpers
|
||||
*/
|
||||
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
|
||||
|
||||
static int eval_int(acpi_handle handle, const char *name, unsigned long *res)
|
||||
{
|
||||
@ -270,116 +242,11 @@ static int exec_sals(acpi_handle handle, unsigned long arg)
|
||||
return exec_simple_method(handle, "SALS", arg);
|
||||
}
|
||||
|
||||
static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
unsigned long long result;
|
||||
union acpi_object in_obj;
|
||||
acpi_status status;
|
||||
|
||||
params.count = 1;
|
||||
params.pointer = &in_obj;
|
||||
in_obj.type = ACPI_TYPE_INTEGER;
|
||||
in_obj.integer.value = arg;
|
||||
|
||||
status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
if (res)
|
||||
*res = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res)
|
||||
{
|
||||
return eval_int_with_arg(handle, "DYTC", cmd, res);
|
||||
}
|
||||
|
||||
static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
|
||||
{
|
||||
return eval_int_with_arg(handle, "VPCR", cmd, res);
|
||||
}
|
||||
|
||||
static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj[2];
|
||||
acpi_status status;
|
||||
|
||||
params.count = 2;
|
||||
params.pointer = in_obj;
|
||||
in_obj[0].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[0].integer.value = cmd;
|
||||
in_obj[1].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[1].integer.value = data;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return eval_vpcr(handle, 0, data);
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 0, data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs
|
||||
*/
|
||||
@ -1918,7 +1785,7 @@ input_failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ideapad_acpi_remove(struct platform_device *pdev)
|
||||
static void ideapad_acpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
|
||||
int i;
|
||||
@ -1939,8 +1806,6 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
|
||||
ideapad_input_exit(priv);
|
||||
ideapad_debugfs_exit(priv);
|
||||
ideapad_sysfs_exit(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -1967,7 +1832,7 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
|
||||
|
||||
static struct platform_driver ideapad_acpi_driver = {
|
||||
.probe = ideapad_acpi_add,
|
||||
.remove = ideapad_acpi_remove,
|
||||
.remove_new = ideapad_acpi_remove,
|
||||
.driver = {
|
||||
.name = "ideapad_acpi",
|
||||
.pm = &ideapad_pm,
|
||||
|
152
drivers/platform/x86/ideapad-laptop.h
Normal file
152
drivers/platform/x86/ideapad-laptop.h
Normal file
@ -0,0 +1,152 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* ideapad-laptop.h - Lenovo IdeaPad ACPI Extras
|
||||
*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
|
||||
*/
|
||||
|
||||
#ifndef _IDEAPAD_LAPTOP_H_
|
||||
#define _IDEAPAD_LAPTOP_H_
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
enum {
|
||||
VPCCMD_R_VPC1 = 0x10,
|
||||
VPCCMD_R_BL_MAX,
|
||||
VPCCMD_R_BL,
|
||||
VPCCMD_W_BL,
|
||||
VPCCMD_R_WIFI,
|
||||
VPCCMD_W_WIFI,
|
||||
VPCCMD_R_BT,
|
||||
VPCCMD_W_BT,
|
||||
VPCCMD_R_BL_POWER,
|
||||
VPCCMD_R_NOVO,
|
||||
VPCCMD_R_VPC2,
|
||||
VPCCMD_R_TOUCHPAD,
|
||||
VPCCMD_W_TOUCHPAD,
|
||||
VPCCMD_R_CAMERA,
|
||||
VPCCMD_W_CAMERA,
|
||||
VPCCMD_R_3G,
|
||||
VPCCMD_W_3G,
|
||||
VPCCMD_R_ODD, /* 0x21 */
|
||||
VPCCMD_W_FAN,
|
||||
VPCCMD_R_RF,
|
||||
VPCCMD_W_RF,
|
||||
VPCCMD_W_YMC = 0x2A,
|
||||
VPCCMD_R_FAN = 0x2B,
|
||||
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
|
||||
VPCCMD_W_BL_POWER = 0x33,
|
||||
};
|
||||
|
||||
static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
unsigned long long result;
|
||||
union acpi_object in_obj;
|
||||
acpi_status status;
|
||||
|
||||
params.count = 1;
|
||||
params.pointer = &in_obj;
|
||||
in_obj.type = ACPI_TYPE_INTEGER;
|
||||
in_obj.integer.value = arg;
|
||||
|
||||
status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
if (res)
|
||||
*res = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
|
||||
{
|
||||
return eval_int_with_arg(handle, "VPCR", cmd, res);
|
||||
}
|
||||
|
||||
static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj[2];
|
||||
acpi_status status;
|
||||
|
||||
params.count = 2;
|
||||
params.pointer = in_obj;
|
||||
in_obj[0].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[0].integer.value = cmd;
|
||||
in_obj[1].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[1].integer.value = data;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
|
||||
|
||||
static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return eval_vpcr(handle, 0, data);
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 0, data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
#undef IDEAPAD_EC_TIMEOUT
|
||||
#endif /* !_IDEAPAD_LAPTOP_H_ */
|
@ -80,6 +80,16 @@ config INTEL_BXTWC_PMIC_TMU
|
||||
This driver enables the alarm wakeup functionality in the TMU unit of
|
||||
Whiskey Cove PMIC.
|
||||
|
||||
config INTEL_BYTCRC_PWRSRC
|
||||
tristate "Intel Bay Trail Crystal Cove power source driver"
|
||||
depends on INTEL_SOC_PMIC
|
||||
help
|
||||
This option adds a power source driver for Crystal Cove PMICs
|
||||
on Intel Bay Trail devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called intel_bytcrc_pwrsrc.
|
||||
|
||||
config INTEL_CHTDC_TI_PWRBTN
|
||||
tristate "Intel Cherry Trail Dollar Cove TI power button driver"
|
||||
depends on INTEL_SOC_PMIC_CHTDC_TI
|
||||
|
@ -38,6 +38,8 @@ intel_bxtwc_tmu-y := bxtwc_tmu.o
|
||||
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
|
||||
intel_crystal_cove_charger-y := crystal_cove_charger.o
|
||||
obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o
|
||||
intel_bytcrc_pwrsrc-y := bytcrc_pwrsrc.o
|
||||
obj-$(CONFIG_INTEL_BYTCRC_PWRSRC) += intel_bytcrc_pwrsrc.o
|
||||
intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o
|
||||
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
|
||||
intel_chtwc_int33fe-y := chtwc_int33fe.o
|
||||
|
@ -89,7 +89,7 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bxt_wcove_tmu_remove(struct platform_device *pdev)
|
||||
static void bxt_wcove_tmu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wcove_tmu *wctmu = platform_get_drvdata(pdev);
|
||||
unsigned int val;
|
||||
@ -101,7 +101,6 @@ static int bxt_wcove_tmu_remove(struct platform_device *pdev)
|
||||
regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val);
|
||||
regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG,
|
||||
val | BXTWC_TMU_ALRM_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -132,7 +131,7 @@ MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table);
|
||||
|
||||
static struct platform_driver bxt_wcove_tmu_driver = {
|
||||
.probe = bxt_wcove_tmu_probe,
|
||||
.remove = bxt_wcove_tmu_remove,
|
||||
.remove_new = bxt_wcove_tmu_remove,
|
||||
.driver = {
|
||||
.name = "bxt_wcove_tmu",
|
||||
.pm = &bxtwc_tmu_pm_ops,
|
||||
|
181
drivers/platform/x86/intel/bytcrc_pwrsrc.c
Normal file
181
drivers/platform/x86/intel/bytcrc_pwrsrc.c
Normal file
@ -0,0 +1,181 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Power-source driver for Bay Trail Crystal Cove PMIC
|
||||
*
|
||||
* Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Based on intel_crystalcove_pwrsrc.c from Android kernel sources, which is:
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define CRYSTALCOVE_SPWRSRC_REG 0x1E
|
||||
#define CRYSTALCOVE_RESETSRC0_REG 0x20
|
||||
#define CRYSTALCOVE_RESETSRC1_REG 0x21
|
||||
#define CRYSTALCOVE_WAKESRC_REG 0x22
|
||||
|
||||
struct crc_pwrsrc_data {
|
||||
struct regmap *regmap;
|
||||
struct dentry *debug_dentry;
|
||||
unsigned int resetsrc0;
|
||||
unsigned int resetsrc1;
|
||||
unsigned int wakesrc;
|
||||
};
|
||||
|
||||
static const char * const pwrsrc_pwrsrc_info[] = {
|
||||
/* bit 0 */ "USB",
|
||||
/* bit 1 */ "DC in",
|
||||
/* bit 2 */ "Battery",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const pwrsrc_resetsrc0_info[] = {
|
||||
/* bit 0 */ "SOC reporting a thermal event",
|
||||
/* bit 1 */ "critical PMIC temperature",
|
||||
/* bit 2 */ "critical system temperature",
|
||||
/* bit 3 */ "critical battery temperature",
|
||||
/* bit 4 */ "VSYS under voltage",
|
||||
/* bit 5 */ "VSYS over voltage",
|
||||
/* bit 6 */ "battery removal",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const pwrsrc_resetsrc1_info[] = {
|
||||
/* bit 0 */ "VCRIT threshold",
|
||||
/* bit 1 */ "BATID reporting battery removal",
|
||||
/* bit 2 */ "user pressing the power button",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const pwrsrc_wakesrc_info[] = {
|
||||
/* bit 0 */ "user pressing the power button",
|
||||
/* bit 1 */ "a battery insertion",
|
||||
/* bit 2 */ "a USB charger insertion",
|
||||
/* bit 3 */ "an adapter insertion",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void crc_pwrsrc_log(struct seq_file *seq, const char *prefix,
|
||||
const char * const *info, unsigned int reg_val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; info[i]; i++) {
|
||||
if (reg_val & BIT(i))
|
||||
seq_printf(seq, "%s by %s\n", prefix, info[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int pwrsrc_show(struct seq_file *seq, void *unused)
|
||||
{
|
||||
struct crc_pwrsrc_data *data = seq->private;
|
||||
unsigned int reg_val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
crc_pwrsrc_log(seq, "System powered", pwrsrc_pwrsrc_info, reg_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resetsrc_show(struct seq_file *seq, void *unused)
|
||||
{
|
||||
struct crc_pwrsrc_data *data = seq->private;
|
||||
|
||||
crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc0_info, data->resetsrc0);
|
||||
crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc1_info, data->resetsrc1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wakesrc_show(struct seq_file *seq, void *unused)
|
||||
{
|
||||
struct crc_pwrsrc_data *data = seq->private;
|
||||
|
||||
crc_pwrsrc_log(seq, "Last wake caused", pwrsrc_wakesrc_info, data->wakesrc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(pwrsrc);
|
||||
DEFINE_SHOW_ATTRIBUTE(resetsrc);
|
||||
DEFINE_SHOW_ATTRIBUTE(wakesrc);
|
||||
|
||||
static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data,
|
||||
unsigned int reg, unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_write(data->regmap, reg, *val);
|
||||
}
|
||||
|
||||
static int crc_pwrsrc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
|
||||
struct crc_pwrsrc_data *data;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->regmap = pmic->regmap;
|
||||
|
||||
/*
|
||||
* Read + clear resetsrc0/1 and wakesrc now, so that they get
|
||||
* cleared even if the debugfs interface is never used.
|
||||
*
|
||||
* Properly clearing the wakesrc is important, leaving bit 0 of it
|
||||
* set turns reboot into poweroff on some tablets.
|
||||
*/
|
||||
ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC0_REG, &data->resetsrc0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC1_REG, &data->resetsrc1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_WAKESRC_REG, &data->wakesrc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops);
|
||||
debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops);
|
||||
debugfs_create_file("wakesrc", 0444, data->debug_dentry, data, &wakesrc_fops);
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crc_pwrsrc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct crc_pwrsrc_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
debugfs_remove_recursive(data->debug_dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver crc_pwrsrc_driver = {
|
||||
.probe = crc_pwrsrc_probe,
|
||||
.remove = crc_pwrsrc_remove,
|
||||
.driver = {
|
||||
.name = "crystal_cove_pwrsrc",
|
||||
},
|
||||
};
|
||||
module_platform_driver(crc_pwrsrc_driver);
|
||||
|
||||
MODULE_ALIAS("platform:crystal_cove_pwrsrc");
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Power-source driver for Bay Trail Crystal Cove PMIC");
|
||||
MODULE_LICENSE("GPL");
|
@ -67,11 +67,10 @@ static int chtdc_ti_pwrbtn_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chtdc_ti_pwrbtn_remove(struct platform_device *pdev)
|
||||
static void chtdc_ti_pwrbtn_remove(struct platform_device *pdev)
|
||||
{
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id chtdc_ti_pwrbtn_id_table[] = {
|
||||
@ -85,7 +84,7 @@ static struct platform_driver chtdc_ti_pwrbtn_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = chtdc_ti_pwrbtn_probe,
|
||||
.remove = chtdc_ti_pwrbtn_remove,
|
||||
.remove_new = chtdc_ti_pwrbtn_remove,
|
||||
.id_table = chtdc_ti_pwrbtn_id_table,
|
||||
};
|
||||
module_platform_driver(chtdc_ti_pwrbtn_driver);
|
||||
|
@ -405,7 +405,7 @@ out_remove_nodes:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cht_int33fe_typec_remove(struct platform_device *pdev)
|
||||
static void cht_int33fe_typec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cht_int33fe_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
@ -414,8 +414,6 @@ static int cht_int33fe_typec_remove(struct platform_device *pdev)
|
||||
i2c_unregister_device(data->battery_fg);
|
||||
|
||||
cht_int33fe_remove_nodes(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
|
||||
@ -429,7 +427,7 @@ static struct platform_driver cht_int33fe_typec_driver = {
|
||||
.acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
|
||||
},
|
||||
.probe = cht_int33fe_typec_probe,
|
||||
.remove = cht_int33fe_typec_remove,
|
||||
.remove_new = cht_int33fe_typec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(cht_int33fe_typec_driver);
|
||||
|
@ -720,7 +720,7 @@ err_remove_notify:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int intel_hid_remove(struct platform_device *device)
|
||||
static void intel_hid_remove(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
|
||||
@ -728,12 +728,6 @@ static int intel_hid_remove(struct platform_device *device)
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
intel_hid_set_enable(&device->dev, false);
|
||||
intel_button_array_enable(&device->dev, false);
|
||||
|
||||
/*
|
||||
* Even if we failed to shut off the event stream, we can still
|
||||
* safely detach from the device.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver intel_hid_pl_driver = {
|
||||
@ -743,7 +737,7 @@ static struct platform_driver intel_hid_pl_driver = {
|
||||
.pm = &intel_hid_pl_pm_ops,
|
||||
},
|
||||
.probe = intel_hid_probe,
|
||||
.remove = intel_hid_remove,
|
||||
.remove_new = intel_hid_remove,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -16,27 +16,63 @@
|
||||
|
||||
static const struct x86_cpu_id ifs_cpu_ids[] __initconst = {
|
||||
X86_MATCH(SAPPHIRERAPIDS_X),
|
||||
X86_MATCH(EMERALDRAPIDS_X),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids);
|
||||
|
||||
static struct ifs_device ifs_device = {
|
||||
.data = {
|
||||
.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT,
|
||||
.test_num = 0,
|
||||
ATTRIBUTE_GROUPS(plat_ifs);
|
||||
ATTRIBUTE_GROUPS(plat_ifs_array);
|
||||
|
||||
bool *ifs_pkg_auth;
|
||||
|
||||
static const struct ifs_test_caps scan_test = {
|
||||
.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT,
|
||||
.test_num = IFS_TYPE_SAF,
|
||||
};
|
||||
|
||||
static const struct ifs_test_caps array_test = {
|
||||
.integrity_cap_bit = MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT,
|
||||
.test_num = IFS_TYPE_ARRAY_BIST,
|
||||
};
|
||||
|
||||
static struct ifs_device ifs_devices[] = {
|
||||
[IFS_TYPE_SAF] = {
|
||||
.test_caps = &scan_test,
|
||||
.misc = {
|
||||
.name = "intel_ifs_0",
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.groups = plat_ifs_groups,
|
||||
},
|
||||
},
|
||||
.misc = {
|
||||
.name = "intel_ifs_0",
|
||||
.nodename = "intel_ifs/0",
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
[IFS_TYPE_ARRAY_BIST] = {
|
||||
.test_caps = &array_test,
|
||||
.misc = {
|
||||
.name = "intel_ifs_1",
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.groups = plat_ifs_array_groups,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#define IFS_NUMTESTS ARRAY_SIZE(ifs_devices)
|
||||
|
||||
static void ifs_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IFS_NUMTESTS; i++) {
|
||||
if (ifs_devices[i].misc.this_device)
|
||||
misc_deregister(&ifs_devices[i].misc);
|
||||
}
|
||||
kfree(ifs_pkg_auth);
|
||||
}
|
||||
|
||||
static int __init ifs_init(void)
|
||||
{
|
||||
const struct x86_cpu_id *m;
|
||||
u64 msrval;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
m = x86_match_cpu(ifs_cpu_ids);
|
||||
if (!m)
|
||||
@ -51,28 +87,27 @@ static int __init ifs_init(void)
|
||||
if (rdmsrl_safe(MSR_INTEGRITY_CAPS, &msrval))
|
||||
return -ENODEV;
|
||||
|
||||
ifs_device.misc.groups = ifs_get_groups();
|
||||
|
||||
if (!(msrval & BIT(ifs_device.data.integrity_cap_bit)))
|
||||
return -ENODEV;
|
||||
|
||||
ifs_device.data.pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL);
|
||||
if (!ifs_device.data.pkg_auth)
|
||||
ifs_pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL);
|
||||
if (!ifs_pkg_auth)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = misc_register(&ifs_device.misc);
|
||||
if (ret) {
|
||||
kfree(ifs_device.data.pkg_auth);
|
||||
return ret;
|
||||
for (i = 0; i < IFS_NUMTESTS; i++) {
|
||||
if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit)))
|
||||
continue;
|
||||
ret = misc_register(&ifs_devices[i].misc);
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
ifs_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ifs_exit(void)
|
||||
{
|
||||
misc_deregister(&ifs_device.misc);
|
||||
kfree(ifs_device.data.pkg_auth);
|
||||
ifs_cleanup();
|
||||
}
|
||||
|
||||
module_init(ifs_init);
|
||||
|
@ -17,7 +17,7 @@
|
||||
* In Field Scan (IFS) is a hardware feature to run circuit level tests on
|
||||
* a CPU core to detect problems that are not caught by parity or ECC checks.
|
||||
* Future CPUs will support more than one type of test which will show up
|
||||
* with a new platform-device instance-id, for now only .0 is exposed.
|
||||
* with a new platform-device instance-id.
|
||||
*
|
||||
*
|
||||
* IFS Image
|
||||
@ -25,7 +25,10 @@
|
||||
*
|
||||
* Intel provides a firmware file containing the scan tests via
|
||||
* github [#f1]_. Similar to microcode there is a separate file for each
|
||||
* family-model-stepping.
|
||||
* family-model-stepping. IFS Images are not applicable for some test types.
|
||||
* Wherever applicable the sysfs directory would provide a "current_batch" file
|
||||
* (see below) for loading the image.
|
||||
*
|
||||
*
|
||||
* IFS Image Loading
|
||||
* -----------------
|
||||
@ -35,7 +38,7 @@
|
||||
* SHA hashes for the test. Then the tests themselves. Status MSRs provide
|
||||
* feedback on the success/failure of these steps.
|
||||
*
|
||||
* The test files are kept in a fixed location: /lib/firmware/intel/ifs_0/
|
||||
* The test files are kept in a fixed location: /lib/firmware/intel/ifs_<n>/
|
||||
* For e.g if there are 3 test files, they would be named in the following
|
||||
* fashion:
|
||||
* ff-mm-ss-01.scan
|
||||
@ -47,7 +50,7 @@
|
||||
* (e.g 1, 2 or 3 in the above scenario) into the curent_batch file.
|
||||
* To load ff-mm-ss-02.scan, the following command can be used::
|
||||
*
|
||||
* # echo 2 > /sys/devices/virtual/misc/intel_ifs_0/current_batch
|
||||
* # echo 2 > /sys/devices/virtual/misc/intel_ifs_<n>/current_batch
|
||||
*
|
||||
* The above file can also be read to know the currently loaded image.
|
||||
*
|
||||
@ -69,16 +72,16 @@
|
||||
* to migrate those applications to other cores before running a core test.
|
||||
* It may also be necessary to redirect interrupts to other CPUs.
|
||||
*
|
||||
* In all cases reading the SCAN_STATUS MSR provides details on what
|
||||
* In all cases reading the corresponding test's STATUS MSR provides details on what
|
||||
* happened. The driver makes the value of this MSR visible to applications
|
||||
* via the "details" file (see below). Interrupted tests may be restarted.
|
||||
*
|
||||
* The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_0/
|
||||
* The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_<n>/
|
||||
* to control execution:
|
||||
*
|
||||
* Test a specific core::
|
||||
*
|
||||
* # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_0/run_test
|
||||
* # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_<n>/run_test
|
||||
*
|
||||
* when HT is enabled any of the sibling cpu# can be specified to test
|
||||
* its corresponding physical core. Since the tests are per physical core,
|
||||
@ -87,21 +90,21 @@
|
||||
*
|
||||
* For e.g. to test core corresponding to cpu5
|
||||
*
|
||||
* # echo 5 > /sys/devices/virtual/misc/intel_ifs_0/run_test
|
||||
* # echo 5 > /sys/devices/virtual/misc/intel_ifs_<n>/run_test
|
||||
*
|
||||
* Results of the last test is provided in /sys::
|
||||
*
|
||||
* $ cat /sys/devices/virtual/misc/intel_ifs_0/status
|
||||
* $ cat /sys/devices/virtual/misc/intel_ifs_<n>/status
|
||||
* pass
|
||||
*
|
||||
* Status can be one of pass, fail, untested
|
||||
*
|
||||
* Additional details of the last test is provided by the details file::
|
||||
*
|
||||
* $ cat /sys/devices/virtual/misc/intel_ifs_0/details
|
||||
* $ cat /sys/devices/virtual/misc/intel_ifs_<n>/details
|
||||
* 0x8081
|
||||
*
|
||||
* The details file reports the hex value of the SCAN_STATUS MSR.
|
||||
* The details file reports the hex value of the test specific status MSR.
|
||||
* Hardware defined error codes are documented in volume 4 of the Intel
|
||||
* Software Developer's Manual but the error_code field may contain one of
|
||||
* the following driver defined software codes:
|
||||
@ -127,6 +130,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#define MSR_ARRAY_BIST 0x00000105
|
||||
#define MSR_COPY_SCAN_HASHES 0x000002c2
|
||||
#define MSR_SCAN_HASHES_STATUS 0x000002c3
|
||||
#define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4
|
||||
@ -137,6 +141,9 @@
|
||||
#define SCAN_TEST_PASS 1
|
||||
#define SCAN_TEST_FAIL 2
|
||||
|
||||
#define IFS_TYPE_SAF 0
|
||||
#define IFS_TYPE_ARRAY_BIST 1
|
||||
|
||||
/* MSR_SCAN_HASHES_STATUS bit fields */
|
||||
union ifs_scan_hashes_status {
|
||||
u64 data;
|
||||
@ -189,6 +196,17 @@ union ifs_status {
|
||||
};
|
||||
};
|
||||
|
||||
/* MSR_ARRAY_BIST bit fields */
|
||||
union ifs_array {
|
||||
u64 data;
|
||||
struct {
|
||||
u32 array_bitmask;
|
||||
u16 array_bank;
|
||||
u16 rsvd :15;
|
||||
u16 ctrl_result :1;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Driver populated error-codes
|
||||
* 0xFD: Test timed out before completing all the chunks.
|
||||
@ -197,22 +215,22 @@ union ifs_status {
|
||||
#define IFS_SW_TIMEOUT 0xFD
|
||||
#define IFS_SW_PARTIAL_COMPLETION 0xFE
|
||||
|
||||
struct ifs_test_caps {
|
||||
int integrity_cap_bit;
|
||||
int test_num;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ifs_data - attributes related to intel IFS driver
|
||||
* @integrity_cap_bit: MSR_INTEGRITY_CAPS bit enumerating this test
|
||||
* @loaded_version: stores the currently loaded ifs image version.
|
||||
* @pkg_auth: array of bool storing per package auth status
|
||||
* @loaded: If a valid test binary has been loaded into the memory
|
||||
* @loading_error: Error occurred on another CPU while loading image
|
||||
* @valid_chunks: number of chunks which could be validated.
|
||||
* @status: it holds simple status pass/fail/untested
|
||||
* @scan_details: opaque scan status code from h/w
|
||||
* @cur_batch: number indicating the currently loaded test file
|
||||
* @test_num: number indicating the test type
|
||||
*/
|
||||
struct ifs_data {
|
||||
int integrity_cap_bit;
|
||||
bool *pkg_auth;
|
||||
int loaded_version;
|
||||
bool loaded;
|
||||
bool loading_error;
|
||||
@ -220,7 +238,6 @@ struct ifs_data {
|
||||
int status;
|
||||
u64 scan_details;
|
||||
u32 cur_batch;
|
||||
int test_num;
|
||||
};
|
||||
|
||||
struct ifs_work {
|
||||
@ -229,7 +246,8 @@ struct ifs_work {
|
||||
};
|
||||
|
||||
struct ifs_device {
|
||||
struct ifs_data data;
|
||||
const struct ifs_test_caps *test_caps;
|
||||
struct ifs_data rw_data;
|
||||
struct miscdevice misc;
|
||||
};
|
||||
|
||||
@ -238,11 +256,21 @@ static inline struct ifs_data *ifs_get_data(struct device *dev)
|
||||
struct miscdevice *m = dev_get_drvdata(dev);
|
||||
struct ifs_device *d = container_of(m, struct ifs_device, misc);
|
||||
|
||||
return &d->data;
|
||||
return &d->rw_data;
|
||||
}
|
||||
|
||||
static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev)
|
||||
{
|
||||
struct miscdevice *m = dev_get_drvdata(dev);
|
||||
struct ifs_device *d = container_of(m, struct ifs_device, misc);
|
||||
|
||||
return d->test_caps;
|
||||
}
|
||||
|
||||
extern bool *ifs_pkg_auth;
|
||||
int ifs_load_firmware(struct device *dev);
|
||||
int do_core_test(int cpu, struct device *dev);
|
||||
const struct attribute_group **ifs_get_groups(void);
|
||||
extern struct attribute *plat_ifs_attrs[];
|
||||
extern struct attribute *plat_ifs_array_attrs[];
|
||||
|
||||
#endif
|
||||
|
@ -192,7 +192,7 @@ static int scan_chunks_sanity_check(struct device *dev)
|
||||
struct ifs_work local_work;
|
||||
int curr_pkg, cpu, ret;
|
||||
|
||||
memset(ifsd->pkg_auth, 0, (topology_max_packages() * sizeof(bool)));
|
||||
memset(ifs_pkg_auth, 0, (topology_max_packages() * sizeof(bool)));
|
||||
ret = validate_ifs_metadata(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -204,7 +204,7 @@ static int scan_chunks_sanity_check(struct device *dev)
|
||||
cpus_read_lock();
|
||||
for_each_online_cpu(cpu) {
|
||||
curr_pkg = topology_physical_package_id(cpu);
|
||||
if (ifsd->pkg_auth[curr_pkg])
|
||||
if (ifs_pkg_auth[curr_pkg])
|
||||
continue;
|
||||
reinit_completion(&ifs_done);
|
||||
local_work.dev = dev;
|
||||
@ -215,7 +215,7 @@ static int scan_chunks_sanity_check(struct device *dev)
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ifsd->pkg_auth[curr_pkg] = 1;
|
||||
ifs_pkg_auth[curr_pkg] = 1;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
@ -257,13 +257,14 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_
|
||||
*/
|
||||
int ifs_load_firmware(struct device *dev)
|
||||
{
|
||||
const struct ifs_test_caps *test = ifs_get_test_caps(dev);
|
||||
struct ifs_data *ifsd = ifs_get_data(dev);
|
||||
const struct firmware *fw;
|
||||
char scan_path[64];
|
||||
int ret = -EINVAL;
|
||||
|
||||
snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan",
|
||||
ifsd->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
|
||||
test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
|
||||
boot_cpu_data.x86_stepping, ifsd->cur_batch);
|
||||
|
||||
ret = request_firmware_direct(&fw, scan_path, dev);
|
||||
|
@ -229,6 +229,85 @@ static void ifs_test_core(int cpu, struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
#define SPINUNIT 100 /* 100 nsec */
|
||||
static atomic_t array_cpus_out;
|
||||
|
||||
/*
|
||||
* Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
|
||||
*/
|
||||
static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
const struct cpumask *smt_mask = cpu_smt_mask(cpu);
|
||||
int all_cpus = cpumask_weight(smt_mask);
|
||||
|
||||
atomic_inc(t);
|
||||
while (atomic_read(t) < all_cpus) {
|
||||
if (timeout < SPINUNIT)
|
||||
return;
|
||||
ndelay(SPINUNIT);
|
||||
timeout -= SPINUNIT;
|
||||
touch_nmi_watchdog();
|
||||
}
|
||||
}
|
||||
|
||||
static int do_array_test(void *data)
|
||||
{
|
||||
union ifs_array *command = data;
|
||||
int cpu = smp_processor_id();
|
||||
int first;
|
||||
|
||||
/*
|
||||
* Only one logical CPU on a core needs to trigger the Array test via MSR write.
|
||||
*/
|
||||
first = cpumask_first(cpu_smt_mask(cpu));
|
||||
|
||||
if (cpu == first) {
|
||||
wrmsrl(MSR_ARRAY_BIST, command->data);
|
||||
/* Pass back the result of the test */
|
||||
rdmsrl(MSR_ARRAY_BIST, command->data);
|
||||
}
|
||||
|
||||
/* Tests complete faster if the sibling is spinning here */
|
||||
wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ifs_array_test_core(int cpu, struct device *dev)
|
||||
{
|
||||
union ifs_array command = {};
|
||||
bool timed_out = false;
|
||||
struct ifs_data *ifsd;
|
||||
unsigned long timeout;
|
||||
|
||||
ifsd = ifs_get_data(dev);
|
||||
|
||||
command.array_bitmask = ~0U;
|
||||
timeout = jiffies + HZ / 2;
|
||||
|
||||
do {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
timed_out = true;
|
||||
break;
|
||||
}
|
||||
atomic_set(&array_cpus_out, 0);
|
||||
stop_core_cpuslocked(cpu, do_array_test, &command);
|
||||
|
||||
if (command.ctrl_result)
|
||||
break;
|
||||
} while (command.array_bitmask);
|
||||
|
||||
ifsd->scan_details = command.data;
|
||||
|
||||
if (command.ctrl_result)
|
||||
ifsd->status = SCAN_TEST_FAIL;
|
||||
else if (timed_out || command.array_bitmask)
|
||||
ifsd->status = SCAN_NOT_TESTED;
|
||||
else
|
||||
ifsd->status = SCAN_TEST_PASS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate per core test. It wakes up work queue threads on the target cpu and
|
||||
* its sibling cpu. Once all sibling threads wake up, the scan test gets executed and
|
||||
@ -236,6 +315,8 @@ static void ifs_test_core(int cpu, struct device *dev)
|
||||
*/
|
||||
int do_core_test(int cpu, struct device *dev)
|
||||
{
|
||||
const struct ifs_test_caps *test = ifs_get_test_caps(dev);
|
||||
struct ifs_data *ifsd = ifs_get_data(dev);
|
||||
int ret = 0;
|
||||
|
||||
/* Prevent CPUs from being taken offline during the scan test */
|
||||
@ -247,7 +328,18 @@ int do_core_test(int cpu, struct device *dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ifs_test_core(cpu, dev);
|
||||
switch (test->test_num) {
|
||||
case IFS_TYPE_SAF:
|
||||
if (!ifsd->loaded)
|
||||
return -EPERM;
|
||||
ifs_test_core(cpu, dev);
|
||||
break;
|
||||
case IFS_TYPE_ARRAY_BIST:
|
||||
ifs_array_test_core(cpu, dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
out:
|
||||
cpus_read_unlock();
|
||||
return ret;
|
||||
|
@ -64,7 +64,6 @@ static ssize_t run_test_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ifs_data *ifsd = ifs_get_data(dev);
|
||||
unsigned int cpu;
|
||||
int rc;
|
||||
|
||||
@ -75,10 +74,7 @@ static ssize_t run_test_store(struct device *dev,
|
||||
if (down_interruptible(&ifs_sem))
|
||||
return -EINTR;
|
||||
|
||||
if (!ifsd->loaded)
|
||||
rc = -EPERM;
|
||||
else
|
||||
rc = do_core_test(cpu, dev);
|
||||
rc = do_core_test(cpu, dev);
|
||||
|
||||
up(&ifs_sem);
|
||||
|
||||
@ -141,7 +137,7 @@ static ssize_t image_version_show(struct device *dev,
|
||||
static DEVICE_ATTR_RO(image_version);
|
||||
|
||||
/* global scan sysfs attributes */
|
||||
static struct attribute *plat_ifs_attrs[] = {
|
||||
struct attribute *plat_ifs_attrs[] = {
|
||||
&dev_attr_details.attr,
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_run_test.attr,
|
||||
@ -150,9 +146,10 @@ static struct attribute *plat_ifs_attrs[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(plat_ifs);
|
||||
|
||||
const struct attribute_group **ifs_get_groups(void)
|
||||
{
|
||||
return plat_ifs_groups;
|
||||
}
|
||||
/* global array sysfs attributes */
|
||||
struct attribute *plat_ifs_array_attrs[] = {
|
||||
&dev_attr_details.attr,
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_run_test.attr,
|
||||
NULL
|
||||
};
|
||||
|
@ -223,11 +223,10 @@ static int int0002_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int0002_remove(struct platform_device *pdev)
|
||||
static void int0002_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
acpi_unregister_wakeup_handler(int0002_check_wake, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int0002_suspend(struct device *dev)
|
||||
@ -273,7 +272,7 @@ static struct platform_driver int0002_driver = {
|
||||
.pm = &int0002_pm_ops,
|
||||
},
|
||||
.probe = int0002_probe,
|
||||
.remove = int0002_remove,
|
||||
.remove_new = int0002_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(int0002_driver);
|
||||
|
@ -292,7 +292,7 @@ r_free:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sar_remove(struct platform_device *device)
|
||||
static void sar_remove(struct platform_device *device)
|
||||
{
|
||||
struct wwan_sar_context *context = dev_get_drvdata(&device->dev);
|
||||
int reg;
|
||||
@ -304,12 +304,11 @@ static int sar_remove(struct platform_device *device)
|
||||
kfree(context->config_data[reg].device_mode_info);
|
||||
|
||||
kfree(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sar_driver = {
|
||||
.probe = sar_probe,
|
||||
.remove = sar_remove,
|
||||
.remove_new = sar_remove,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.acpi_match_table = ACPI_PTR(sar_device_ids)
|
||||
|
@ -317,7 +317,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_int3472_discrete_remove(struct platform_device *pdev)
|
||||
static void skl_int3472_discrete_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev);
|
||||
|
||||
@ -326,8 +326,6 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev)
|
||||
skl_int3472_unregister_clock(int3472);
|
||||
skl_int3472_unregister_pled(int3472);
|
||||
skl_int3472_unregister_regulator(int3472);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_int3472_discrete_probe(struct platform_device *pdev)
|
||||
@ -392,7 +390,7 @@ static struct platform_driver int3472_discrete = {
|
||||
.acpi_match_table = int3472_device_id,
|
||||
},
|
||||
.probe = skl_int3472_discrete_probe,
|
||||
.remove = skl_int3472_discrete_remove,
|
||||
.remove_new = skl_int3472_discrete_remove,
|
||||
};
|
||||
module_platform_driver(int3472_discrete);
|
||||
|
||||
|
@ -78,13 +78,12 @@ static int mrfld_pwrbtn_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrfld_pwrbtn_remove(struct platform_device *pdev)
|
||||
static void mrfld_pwrbtn_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
dev_pm_clear_wake_irq(dev);
|
||||
device_init_wakeup(dev, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id mrfld_pwrbtn_id_table[] = {
|
||||
@ -98,7 +97,7 @@ static struct platform_driver mrfld_pwrbtn_driver = {
|
||||
.name = "mrfld_bcove_pwrbtn",
|
||||
},
|
||||
.probe = mrfld_pwrbtn_probe,
|
||||
.remove = mrfld_pwrbtn_remove,
|
||||
.remove_new = mrfld_pwrbtn_remove,
|
||||
.id_table = mrfld_pwrbtn_id_table,
|
||||
};
|
||||
module_platform_driver(mrfld_pwrbtn_driver);
|
||||
|
@ -1160,7 +1160,7 @@ static int pmc_core_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc_core_remove(struct platform_device *pdev)
|
||||
static void pmc_core_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1168,7 +1168,6 @@ static int pmc_core_remove(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
mutex_destroy(&pmcdev->lock);
|
||||
iounmap(pmcdev->regbase);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool warn_on_s0ix_failures;
|
||||
@ -1275,7 +1274,7 @@ static struct platform_driver pmc_core_driver = {
|
||||
.dev_groups = pmc_dev_groups,
|
||||
},
|
||||
.probe = pmc_core_probe,
|
||||
.remove = pmc_core_remove,
|
||||
.remove_new = pmc_core_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pmc_core_driver);
|
||||
|
@ -8,6 +8,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include "core.h"
|
||||
|
||||
const struct pmc_reg_map mtl_reg_map = {
|
||||
@ -45,8 +46,38 @@ void mtl_core_configure(struct pmc_dev *pmcdev)
|
||||
pmc_core_send_ltr_ignore(pmcdev, 3);
|
||||
}
|
||||
|
||||
#define MTL_GNA_PCI_DEV 0x7e4c
|
||||
#define MTL_IPU_PCI_DEV 0x7d19
|
||||
#define MTL_VPU_PCI_DEV 0x7d1d
|
||||
static void mtl_set_device_d3(unsigned int device)
|
||||
{
|
||||
struct pci_dev *pcidev;
|
||||
|
||||
pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
|
||||
if (pcidev) {
|
||||
if (!device_trylock(&pcidev->dev)) {
|
||||
pci_dev_put(pcidev);
|
||||
return;
|
||||
}
|
||||
if (!pcidev->dev.driver) {
|
||||
dev_info(&pcidev->dev, "Setting to D3hot\n");
|
||||
pci_set_power_state(pcidev, PCI_D3hot);
|
||||
}
|
||||
device_unlock(&pcidev->dev);
|
||||
pci_dev_put(pcidev);
|
||||
}
|
||||
}
|
||||
|
||||
void mtl_core_init(struct pmc_dev *pmcdev)
|
||||
{
|
||||
pmcdev->map = &mtl_reg_map;
|
||||
pmcdev->core_configure = mtl_core_configure;
|
||||
|
||||
/*
|
||||
* Set power state of select devices that do not have drivers to D3
|
||||
* so that they do not block Package C entry.
|
||||
*/
|
||||
mtl_set_device_d3(MTL_GNA_PCI_DEV);
|
||||
mtl_set_device_d3(MTL_IPU_PCI_DEV);
|
||||
mtl_set_device_d3(MTL_VPU_PCI_DEV);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ bool intel_pmt_is_early_client_hw(struct device *dev)
|
||||
*/
|
||||
return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw);
|
||||
EXPORT_SYMBOL_NS_GPL(intel_pmt_is_early_client_hw, INTEL_PMT);
|
||||
|
||||
static inline int
|
||||
pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count)
|
||||
@ -327,7 +327,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa
|
||||
return intel_pmt_dev_register(entry, ns, dev);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmt_dev_create);
|
||||
EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_create, INTEL_PMT);
|
||||
|
||||
void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
|
||||
struct intel_pmt_namespace *ns)
|
||||
@ -343,7 +343,7 @@ void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
|
||||
device_unregister(dev);
|
||||
xa_erase(ns->xa, entry->devid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy);
|
||||
EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_destroy, INTEL_PMT);
|
||||
|
||||
static int __init pmt_class_init(void)
|
||||
{
|
||||
|
@ -328,3 +328,4 @@ module_exit(pmt_crashlog_exit);
|
||||
MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel PMT Crashlog driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(INTEL_PMT);
|
||||
|
@ -78,7 +78,7 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
|
||||
* reserved for future use. They have zero size. Do not fail
|
||||
* probe for these. Just ignore them.
|
||||
*/
|
||||
if (header->size == 0)
|
||||
if (header->size == 0 || header->access_type == 0xF)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@ -160,3 +160,4 @@ module_exit(pmt_telem_exit);
|
||||
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel PMT Telemetry driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(INTEL_PMT);
|
||||
|
@ -49,7 +49,7 @@
|
||||
#define SDSI_MBOX_CMD_SUCCESS 0x40
|
||||
#define SDSI_MBOX_CMD_TIMEOUT 0x80
|
||||
|
||||
#define MBOX_TIMEOUT_US 2000
|
||||
#define MBOX_TIMEOUT_US 500000
|
||||
#define MBOX_TIMEOUT_ACQUIRE_US 1000
|
||||
#define MBOX_POLLING_PERIOD_US 100
|
||||
#define MBOX_ACQUIRE_NUM_RETRIES 5
|
||||
|
@ -2,8 +2,12 @@ menu "Intel Speed Select Technology interface support"
|
||||
depends on PCI
|
||||
depends on X86_64 || COMPILE_TEST
|
||||
|
||||
config INTEL_SPEED_SELECT_TPMI
|
||||
tristate
|
||||
|
||||
config INTEL_SPEED_SELECT_INTERFACE
|
||||
tristate "Intel(R) Speed Select Technology interface drivers"
|
||||
select INTEL_SPEED_SELECT_TPMI if INTEL_TPMI
|
||||
help
|
||||
This config enables the Intel(R) Speed Select Technology interface
|
||||
drivers. The Intel(R) speed select technology features are non
|
||||
|
@ -8,3 +8,5 @@ obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
|
||||
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
|
||||
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
|
||||
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o
|
||||
obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi_core.o
|
||||
obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi.o
|
||||
|
@ -19,9 +19,13 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <uapi/linux/isst_if.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
|
||||
#include "isst_if_common.h"
|
||||
|
||||
#define MSR_THREAD_ID_INFO 0x53
|
||||
#define MSR_PM_LOGICAL_ID 0x54
|
||||
#define MSR_CPU_BUS_NUMBER 0x128
|
||||
|
||||
static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
|
||||
@ -31,6 +35,7 @@ static int punit_msr_white_list[] = {
|
||||
MSR_CONFIG_TDP_CONTROL,
|
||||
MSR_TURBO_RATIO_LIMIT1,
|
||||
MSR_TURBO_RATIO_LIMIT2,
|
||||
MSR_PM_LOGICAL_ID,
|
||||
};
|
||||
|
||||
struct isst_valid_cmd_ranges {
|
||||
@ -73,6 +78,8 @@ struct isst_cmd {
|
||||
u32 param;
|
||||
};
|
||||
|
||||
static bool isst_hpm_support;
|
||||
|
||||
static DECLARE_HASHTABLE(isst_hash, 8);
|
||||
static DEFINE_MUTEX(isst_hash_lock);
|
||||
|
||||
@ -262,11 +269,13 @@ bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
|
||||
|
||||
static int isst_if_api_version;
|
||||
|
||||
static int isst_if_get_platform_info(void __user *argp)
|
||||
{
|
||||
struct isst_if_platform_info info;
|
||||
|
||||
info.api_version = ISST_IF_API_VERSION;
|
||||
info.api_version = isst_if_api_version;
|
||||
info.driver_version = ISST_IF_DRIVER_VERSION;
|
||||
info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT;
|
||||
info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
|
||||
@ -409,11 +418,20 @@ static int isst_if_cpu_online(unsigned int cpu)
|
||||
isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
|
||||
}
|
||||
|
||||
if (isst_hpm_support) {
|
||||
|
||||
ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
|
||||
if (!ret)
|
||||
goto set_punit_id;
|
||||
}
|
||||
|
||||
ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
|
||||
if (ret) {
|
||||
isst_cpu_info[cpu].punit_cpu_id = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_punit_id:
|
||||
isst_cpu_info[cpu].punit_cpu_id = data;
|
||||
|
||||
isst_restore_msr_local(cpu);
|
||||
@ -588,6 +606,7 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
|
||||
struct isst_if_cmd_cb cmd_cb;
|
||||
struct isst_if_cmd_cb *cb;
|
||||
long ret = -ENOTTY;
|
||||
int i;
|
||||
|
||||
switch (cmd) {
|
||||
case ISST_IF_GET_PLATFORM_INFO:
|
||||
@ -616,6 +635,16 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
|
||||
ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
|
||||
struct isst_if_cmd_cb *cb = &punit_callbacks[i];
|
||||
int ret;
|
||||
|
||||
if (cb->def_ioctl) {
|
||||
ret = cb->def_ioctl(file, cmd, arg);
|
||||
if (!ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -691,6 +720,12 @@ static struct miscdevice isst_if_char_driver = {
|
||||
.fops = &isst_if_char_driver_ops,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id hpm_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SIERRAFOREST_X, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
static int isst_misc_reg(void)
|
||||
{
|
||||
mutex_lock(&punit_misc_dev_reg_lock);
|
||||
@ -698,6 +733,12 @@ static int isst_misc_reg(void)
|
||||
goto unlock_exit;
|
||||
|
||||
if (!misc_usage_count) {
|
||||
const struct x86_cpu_id *id;
|
||||
|
||||
id = x86_match_cpu(hpm_cpu_ids);
|
||||
if (id)
|
||||
isst_hpm_support = true;
|
||||
|
||||
misc_device_ret = isst_if_cpu_info_init();
|
||||
if (misc_device_ret)
|
||||
goto unlock_exit;
|
||||
@ -756,6 +797,10 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
|
||||
mutex_unlock(&punit_misc_dev_open_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (!cb->api_version)
|
||||
cb->api_version = ISST_IF_API_VERSION;
|
||||
if (cb->api_version > isst_if_api_version)
|
||||
isst_if_api_version = cb->api_version;
|
||||
memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
|
||||
punit_callbacks[device_type].registered = 1;
|
||||
mutex_unlock(&punit_misc_dev_open_lock);
|
||||
|
@ -30,7 +30,8 @@
|
||||
|
||||
#define ISST_IF_DEV_MBOX 0
|
||||
#define ISST_IF_DEV_MMIO 1
|
||||
#define ISST_IF_DEV_MAX 2
|
||||
#define ISST_IF_DEV_TPMI 2
|
||||
#define ISST_IF_DEV_MAX 3
|
||||
|
||||
/**
|
||||
* struct isst_if_cmd_cb - Used to register a IOCTL handler
|
||||
@ -40,6 +41,7 @@
|
||||
* @offset: Offset to the first valid member in command structure.
|
||||
* This will be the offset of the start of the command
|
||||
* after command count field
|
||||
* @api_version: API version supported for this target. 0, if none.
|
||||
* @owner: Registered module owner
|
||||
* @cmd_callback: Callback function to handle IOCTL. The callback has the
|
||||
* command pointer with data for command. There is a pointer
|
||||
@ -47,6 +49,8 @@
|
||||
* response to user ioctl buffer. The "resume" argument
|
||||
* can be used to avoid storing the command for replay
|
||||
* during system resume
|
||||
* @def_ioctl: Default IOCTL handler callback, if there is no match in
|
||||
* the existing list of IOCTL handled by the common handler.
|
||||
*
|
||||
* This structure is used to register an handler for IOCTL. To avoid
|
||||
* code duplication common code handles all the IOCTL command read/write
|
||||
@ -57,8 +61,10 @@ struct isst_if_cmd_cb {
|
||||
int registered;
|
||||
int cmd_size;
|
||||
int offset;
|
||||
int api_version;
|
||||
struct module *owner;
|
||||
long (*cmd_callback)(u8 *ptr, int *write_only, int resume);
|
||||
long (*def_ioctl)(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
};
|
||||
|
||||
/* Internal interface functions */
|
||||
|
72
drivers/platform/x86/intel/speed_select_if/isst_tpmi.c
Normal file
72
drivers/platform/x86/intel/speed_select_if/isst_tpmi.c
Normal file
@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* isst_tpmi.c: SST TPMI interface
|
||||
*
|
||||
* Copyright (c) 2023, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/intel_tpmi.h>
|
||||
|
||||
#include "isst_tpmi_core.h"
|
||||
|
||||
static int intel_sst_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tpmi_sst_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tpmi_sst_dev_add(auxdev);
|
||||
if (ret)
|
||||
tpmi_sst_exit();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_sst_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
tpmi_sst_dev_remove(auxdev);
|
||||
tpmi_sst_exit();
|
||||
}
|
||||
|
||||
static int intel_sst_suspend(struct device *dev)
|
||||
{
|
||||
tpmi_sst_dev_suspend(to_auxiliary_dev(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_sst_resume(struct device *dev)
|
||||
{
|
||||
tpmi_sst_dev_resume(to_auxiliary_dev(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(intel_sst_pm, intel_sst_suspend, intel_sst_resume);
|
||||
|
||||
static const struct auxiliary_device_id intel_sst_id_table[] = {
|
||||
{ .name = "intel_vsec.tpmi-sst" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, intel_sst_id_table);
|
||||
|
||||
static struct auxiliary_driver intel_sst_aux_driver = {
|
||||
.id_table = intel_sst_id_table,
|
||||
.remove = intel_sst_remove,
|
||||
.probe = intel_sst_probe,
|
||||
.driver = {
|
||||
.pm = pm_sleep_ptr(&intel_sst_pm),
|
||||
},
|
||||
};
|
||||
|
||||
module_auxiliary_driver(intel_sst_aux_driver);
|
||||
|
||||
MODULE_IMPORT_NS(INTEL_TPMI_SST);
|
||||
MODULE_DESCRIPTION("Intel TPMI SST Driver");
|
||||
MODULE_LICENSE("GPL");
|
1440
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
Normal file
1440
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
Normal file
File diff suppressed because it is too large
Load Diff
18
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h
Normal file
18
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Intel Speed Select Interface: Drivers Internal defines
|
||||
* Copyright (c) 2023, Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ISST_TPMI_CORE_H
|
||||
#define _ISST_TPMI_CORE_H
|
||||
|
||||
int tpmi_sst_init(void);
|
||||
void tpmi_sst_exit(void);
|
||||
int tpmi_sst_dev_add(struct auxiliary_device *auxdev);
|
||||
void tpmi_sst_dev_remove(struct auxiliary_device *auxdev);
|
||||
void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev);
|
||||
void tpmi_sst_dev_resume(struct auxiliary_device *auxdev);
|
||||
#endif
|
@ -1156,15 +1156,14 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int telemetry_pltdrv_remove(struct platform_device *pdev)
|
||||
static void telemetry_pltdrv_remove(struct platform_device *pdev)
|
||||
{
|
||||
telemetry_clear_pltdata();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver telemetry_soc_driver = {
|
||||
.probe = telemetry_pltdrv_probe,
|
||||
.remove = telemetry_pltdrv_remove,
|
||||
.remove_new = telemetry_pltdrv_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
|
@ -204,6 +204,13 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids);
|
||||
|
@ -325,18 +325,12 @@ static int intel_vbtn_probe(struct platform_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_vbtn_remove(struct platform_device *device)
|
||||
static void intel_vbtn_remove(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
|
||||
device_init_wakeup(&device->dev, false);
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
|
||||
/*
|
||||
* Even if we failed to shut off the event stream, we can still
|
||||
* safely detach from the device.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_vbtn_pm_prepare(struct device *dev)
|
||||
@ -377,7 +371,7 @@ static struct platform_driver intel_vbtn_pl_driver = {
|
||||
.pm = &intel_vbtn_pm_ops,
|
||||
},
|
||||
.probe = intel_vbtn_probe,
|
||||
.remove = intel_vbtn_remove,
|
||||
.remove_new = intel_vbtn_remove,
|
||||
};
|
||||
|
||||
static acpi_status __init
|
||||
|
@ -67,14 +67,6 @@ enum intel_vsec_id {
|
||||
VSEC_ID_TPMI = 66,
|
||||
};
|
||||
|
||||
static enum intel_vsec_id intel_vsec_allow_list[] = {
|
||||
VSEC_ID_TELEMETRY,
|
||||
VSEC_ID_WATCHER,
|
||||
VSEC_ID_CRASHLOG,
|
||||
VSEC_ID_SDSI,
|
||||
VSEC_ID_TPMI,
|
||||
};
|
||||
|
||||
static const char *intel_vsec_name(enum intel_vsec_id id)
|
||||
{
|
||||
switch (id) {
|
||||
@ -98,26 +90,19 @@ static const char *intel_vsec_name(enum intel_vsec_id id)
|
||||
}
|
||||
}
|
||||
|
||||
static bool intel_vsec_allowed(u16 id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++)
|
||||
if (intel_vsec_allow_list[i] == id)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool intel_vsec_disabled(u16 id, unsigned long quirks)
|
||||
static bool intel_vsec_supported(u16 id, unsigned long caps)
|
||||
{
|
||||
switch (id) {
|
||||
case VSEC_ID_TELEMETRY:
|
||||
return !!(caps & VSEC_CAP_TELEMETRY);
|
||||
case VSEC_ID_WATCHER:
|
||||
return !!(quirks & VSEC_QUIRK_NO_WATCHER);
|
||||
|
||||
return !!(caps & VSEC_CAP_WATCHER);
|
||||
case VSEC_ID_CRASHLOG:
|
||||
return !!(quirks & VSEC_QUIRK_NO_CRASHLOG);
|
||||
|
||||
return !!(caps & VSEC_CAP_CRASHLOG);
|
||||
case VSEC_ID_SDSI:
|
||||
return !!(caps & VSEC_CAP_SDSI);
|
||||
case VSEC_ID_TPMI:
|
||||
return !!(caps & VSEC_CAP_TPMI);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -169,11 +154,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
|
||||
|
||||
ret = auxiliary_device_init(auxdev);
|
||||
if (ret < 0) {
|
||||
mutex_lock(&vsec_ida_lock);
|
||||
ida_free(intel_vsec_dev->ida, auxdev->id);
|
||||
mutex_unlock(&vsec_ida_lock);
|
||||
kfree(intel_vsec_dev->resource);
|
||||
kfree(intel_vsec_dev);
|
||||
intel_vsec_dev_release(&auxdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -206,7 +187,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
|
||||
unsigned long quirks = info->quirks;
|
||||
int i;
|
||||
|
||||
if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks))
|
||||
if (!intel_vsec_supported(header->id, info->caps))
|
||||
return -EINVAL;
|
||||
|
||||
if (!header->num_entries) {
|
||||
@ -261,14 +242,14 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
|
||||
static bool intel_vsec_walk_header(struct pci_dev *pdev,
|
||||
struct intel_vsec_platform_info *info)
|
||||
{
|
||||
struct intel_vsec_header **header = info->capabilities;
|
||||
struct intel_vsec_header **header = info->headers;
|
||||
bool have_devices = false;
|
||||
int ret;
|
||||
|
||||
for ( ; *header; header++) {
|
||||
ret = intel_vsec_add_dev(pdev, *header, info);
|
||||
if (ret)
|
||||
dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n",
|
||||
dev_info(&pdev->dev, "Could not add device for VSEC id %d\n",
|
||||
(*header)->id);
|
||||
else
|
||||
have_devices = true;
|
||||
@ -403,14 +384,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TGL info */
|
||||
static const struct intel_vsec_platform_info tgl_info = {
|
||||
.quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG |
|
||||
VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW,
|
||||
};
|
||||
|
||||
/* DG1 info */
|
||||
static struct intel_vsec_header dg1_telemetry = {
|
||||
static struct intel_vsec_header dg1_header = {
|
||||
.length = 0x10,
|
||||
.id = 2,
|
||||
.num_entries = 1,
|
||||
@ -419,19 +394,31 @@ static struct intel_vsec_header dg1_telemetry = {
|
||||
.offset = 0x466000,
|
||||
};
|
||||
|
||||
static struct intel_vsec_header *dg1_capabilities[] = {
|
||||
&dg1_telemetry,
|
||||
static struct intel_vsec_header *dg1_headers[] = {
|
||||
&dg1_header,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct intel_vsec_platform_info dg1_info = {
|
||||
.capabilities = dg1_capabilities,
|
||||
.caps = VSEC_CAP_TELEMETRY,
|
||||
.headers = dg1_headers,
|
||||
.quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW,
|
||||
};
|
||||
|
||||
/* MTL info */
|
||||
static const struct intel_vsec_platform_info mtl_info = {
|
||||
.quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG,
|
||||
.caps = VSEC_CAP_TELEMETRY,
|
||||
};
|
||||
|
||||
/* OOBMSM info */
|
||||
static const struct intel_vsec_platform_info oobmsm_info = {
|
||||
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI,
|
||||
};
|
||||
|
||||
/* TGL info */
|
||||
static const struct intel_vsec_platform_info tgl_info = {
|
||||
.caps = VSEC_CAP_TELEMETRY,
|
||||
.quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW,
|
||||
};
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d
|
||||
@ -446,7 +433,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = {
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) },
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) },
|
||||
{ }
|
||||
|
@ -5,6 +5,12 @@
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
#define VSEC_CAP_TELEMETRY BIT(0)
|
||||
#define VSEC_CAP_WATCHER BIT(1)
|
||||
#define VSEC_CAP_CRASHLOG BIT(2)
|
||||
#define VSEC_CAP_SDSI BIT(3)
|
||||
#define VSEC_CAP_TPMI BIT(4)
|
||||
|
||||
struct pci_dev;
|
||||
struct resource;
|
||||
|
||||
@ -27,7 +33,8 @@ enum intel_vsec_quirks {
|
||||
|
||||
/* Platform specific data */
|
||||
struct intel_vsec_platform_info {
|
||||
struct intel_vsec_header **capabilities;
|
||||
struct intel_vsec_header **headers;
|
||||
unsigned long caps;
|
||||
unsigned long quirks;
|
||||
};
|
||||
|
||||
|
187
drivers/platform/x86/lenovo-ymc.c
Normal file
187
drivers/platform/x86/lenovo-ymc.c
Normal file
@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* lenovo-ymc.c - Lenovo Yoga Mode Control driver
|
||||
*
|
||||
* Copyright © 2022 Gergo Koteles <soyer@irl.hu>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/wmi.h>
|
||||
#include "ideapad-laptop.h"
|
||||
|
||||
#define LENOVO_YMC_EVENT_GUID "06129D99-6083-4164-81AD-F092F9D773A6"
|
||||
#define LENOVO_YMC_QUERY_GUID "09B0EE6E-C3FD-4243-8DA1-7911FF80BB8C"
|
||||
|
||||
#define LENOVO_YMC_QUERY_INSTANCE 0
|
||||
#define LENOVO_YMC_QUERY_METHOD 0x01
|
||||
|
||||
static bool ec_trigger __read_mostly;
|
||||
module_param(ec_trigger, bool, 0444);
|
||||
MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events");
|
||||
|
||||
static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = {
|
||||
{
|
||||
/* Lenovo Yoga 7 14ARB7 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
struct lenovo_ymc_private {
|
||||
struct input_dev *input_dev;
|
||||
struct acpi_device *ec_acpi_dev;
|
||||
};
|
||||
|
||||
static void lenovo_ymc_trigger_ec(struct wmi_device *wdev, struct lenovo_ymc_private *priv)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!priv->ec_acpi_dev)
|
||||
return;
|
||||
|
||||
err = write_ec_cmd(priv->ec_acpi_dev->handle, VPCCMD_W_YMC, 1);
|
||||
if (err)
|
||||
dev_warn(&wdev->dev, "Could not write YMC: %d\n", err);
|
||||
}
|
||||
|
||||
static const struct key_entry lenovo_ymc_keymap[] = {
|
||||
/* Laptop */
|
||||
{ KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
|
||||
/* Tablet */
|
||||
{ KE_SW, 0x02, { .sw = { SW_TABLET_MODE, 1 } } },
|
||||
/* Drawing Board */
|
||||
{ KE_SW, 0x03, { .sw = { SW_TABLET_MODE, 1 } } },
|
||||
/* Tent */
|
||||
{ KE_SW, 0x04, { .sw = { SW_TABLET_MODE, 1 } } },
|
||||
{ KE_END },
|
||||
};
|
||||
|
||||
static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data)
|
||||
{
|
||||
struct lenovo_ymc_private *priv = dev_get_drvdata(&wdev->dev);
|
||||
u32 input_val = 0;
|
||||
struct acpi_buffer input = { sizeof(input_val), &input_val };
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int code;
|
||||
|
||||
status = wmi_evaluate_method(LENOVO_YMC_QUERY_GUID,
|
||||
LENOVO_YMC_QUERY_INSTANCE,
|
||||
LENOVO_YMC_QUERY_METHOD,
|
||||
&input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&wdev->dev,
|
||||
"Failed to evaluate query method: %s\n",
|
||||
acpi_format_exception(status));
|
||||
return;
|
||||
}
|
||||
|
||||
obj = output.pointer;
|
||||
|
||||
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||
dev_warn(&wdev->dev,
|
||||
"WMI event data is not an integer\n");
|
||||
goto free_obj;
|
||||
}
|
||||
code = obj->integer.value;
|
||||
|
||||
if (!sparse_keymap_report_event(priv->input_dev, code, 1, true))
|
||||
dev_warn(&wdev->dev, "Unknown key %d pressed\n", code);
|
||||
|
||||
free_obj:
|
||||
kfree(obj);
|
||||
lenovo_ymc_trigger_ec(wdev, priv);
|
||||
}
|
||||
|
||||
static void acpi_dev_put_helper(void *p) { acpi_dev_put(p); }
|
||||
|
||||
static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
|
||||
{
|
||||
struct lenovo_ymc_private *priv;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table);
|
||||
|
||||
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ec_trigger) {
|
||||
pr_debug("Lenovo YMC enable EC triggering.\n");
|
||||
priv->ec_acpi_dev = acpi_dev_get_first_match_dev("VPC2004", NULL, -1);
|
||||
|
||||
if (!priv->ec_acpi_dev) {
|
||||
dev_err(&wdev->dev, "Could not find EC ACPI device.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
err = devm_add_action_or_reset(&wdev->dev,
|
||||
acpi_dev_put_helper, priv->ec_acpi_dev);
|
||||
if (err) {
|
||||
dev_err(&wdev->dev,
|
||||
"Could not clean up EC ACPI device: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
input_dev = devm_input_allocate_device(&wdev->dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev->name = "Lenovo Yoga Tablet Mode Control switch";
|
||||
input_dev->phys = LENOVO_YMC_EVENT_GUID "/input0";
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->dev.parent = &wdev->dev;
|
||||
err = sparse_keymap_setup(input_dev, lenovo_ymc_keymap, NULL);
|
||||
if (err) {
|
||||
dev_err(&wdev->dev,
|
||||
"Could not set up input device keymap: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err) {
|
||||
dev_err(&wdev->dev,
|
||||
"Could not register input device: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->input_dev = input_dev;
|
||||
dev_set_drvdata(&wdev->dev, priv);
|
||||
|
||||
/* Report the state for the first time on probe */
|
||||
lenovo_ymc_trigger_ec(wdev, priv);
|
||||
lenovo_ymc_notify(wdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id lenovo_ymc_wmi_id_table[] = {
|
||||
{ .guid_string = LENOVO_YMC_EVENT_GUID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(wmi, lenovo_ymc_wmi_id_table);
|
||||
|
||||
static struct wmi_driver lenovo_ymc_driver = {
|
||||
.driver = {
|
||||
.name = "lenovo-ymc",
|
||||
},
|
||||
.id_table = lenovo_ymc_wmi_id_table,
|
||||
.probe = lenovo_ymc_probe,
|
||||
.notify = lenovo_ymc_notify,
|
||||
};
|
||||
|
||||
module_wmi_driver(lenovo_ymc_driver);
|
||||
|
||||
MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>");
|
||||
MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver");
|
||||
MODULE_LICENSE("GPL");
|
897
drivers/platform/x86/msi-ec.c
Normal file
897
drivers/platform/x86/msi-ec.c
Normal file
@ -0,0 +1,897 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/*
|
||||
* msi-ec: MSI laptops' embedded controller driver.
|
||||
*
|
||||
* This driver allows various MSI laptops' functionalities to be
|
||||
* controlled from userspace.
|
||||
*
|
||||
* It contains EC memory configurations for different firmware versions
|
||||
* and exports battery charge thresholds to userspace.
|
||||
*
|
||||
* Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es>
|
||||
* Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev>
|
||||
* Copyright (C) 2023 Nikita Kravets <teackot@gmail.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "msi-ec.h"
|
||||
|
||||
#include <acpi/battery.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
static const char *const SM_ECO_NAME = "eco";
|
||||
static const char *const SM_COMFORT_NAME = "comfort";
|
||||
static const char *const SM_SPORT_NAME = "sport";
|
||||
static const char *const SM_TURBO_NAME = "turbo";
|
||||
|
||||
static const char *const FM_AUTO_NAME = "auto";
|
||||
static const char *const FM_SILENT_NAME = "silent";
|
||||
static const char *const FM_BASIC_NAME = "basic";
|
||||
static const char *const FM_ADVANCED_NAME = "advanced";
|
||||
|
||||
static const char * const ALLOWED_FW_0[] __initconst = {
|
||||
"14C1EMS1.012",
|
||||
"14C1EMS1.101",
|
||||
"14C1EMS1.102",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF0 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_0,
|
||||
.charge_control = {
|
||||
.address = 0xef,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = 0x2f,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = 0xbf,
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xf2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = {
|
||||
.address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xf4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_BASIC_NAME, 0x4d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68,
|
||||
.rt_fan_speed_address = 0x71,
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = 0x89,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = 0x80,
|
||||
.rt_fan_speed_address = 0x89,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = 0x2b,
|
||||
.mute_led_address = 0x2c,
|
||||
.bit = 2,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = 0x2c, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = 0xf3,
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_1[] __initconst = {
|
||||
"17F2EMS1.103",
|
||||
"17F2EMS1.104",
|
||||
"17F2EMS1.106",
|
||||
"17F2EMS1.107",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF1 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_1,
|
||||
.charge_control = {
|
||||
.address = 0xef,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = 0x2f,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = 0xbf,
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xf2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
{ SM_TURBO_NAME, 0xc4 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = {
|
||||
.address = MSI_EC_ADDR_UNKNOWN,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xf4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_BASIC_NAME, 0x4d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68,
|
||||
.rt_fan_speed_address = 0x71,
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = 0x89,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = 0x80,
|
||||
.rt_fan_speed_address = 0x89,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = 0x2b,
|
||||
.mute_led_address = 0x2c,
|
||||
.bit = 2,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = 0x2c, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = 0xf3,
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_2[] __initconst = {
|
||||
"1552EMS1.118",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF2 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_2,
|
||||
.charge_control = {
|
||||
.address = 0xd7,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = 0x2f,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = 0xe8,
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xf2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = {
|
||||
.address = 0xeb,
|
||||
.mask = 0x0f,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xd4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_BASIC_NAME, 0x4d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68,
|
||||
.rt_fan_speed_address = 0x71,
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = 0x89,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = 0x80,
|
||||
.rt_fan_speed_address = 0x89,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = 0x2c,
|
||||
.mute_led_address = 0x2d,
|
||||
.bit = 1,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = 0x2c, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = 0xd3,
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_3[] __initconst = {
|
||||
"1592EMS1.111",
|
||||
"E1592IMS.10C",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF3 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_3,
|
||||
.charge_control = {
|
||||
.address = 0xef,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = 0x2f,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = 0xe8,
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xd2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = {
|
||||
.address = 0xeb,
|
||||
.mask = 0x0f,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xd4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_BASIC_NAME, 0x4d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68,
|
||||
.rt_fan_speed_address = 0xc9,
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = 0x89, // ?
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = 0x80,
|
||||
.rt_fan_speed_address = 0x89,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = 0x2b,
|
||||
.mute_led_address = 0x2c,
|
||||
.bit = 1,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = 0x2c, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = 0xd3,
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_4[] __initconst = {
|
||||
"16V4EMS1.114",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF4 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_4,
|
||||
.charge_control = {
|
||||
.address = 0xd7,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = 0x2f,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xd2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = { // may be supported, but address is unknown
|
||||
.address = MSI_EC_ADDR_UNKNOWN,
|
||||
.mask = 0x0f,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xd4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68, // needs testing
|
||||
.rt_fan_speed_address = 0x71, // needs testing
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = 0x80,
|
||||
.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = MSI_EC_ADDR_UNKNOWN,
|
||||
.mute_led_address = MSI_EC_ADDR_UNKNOWN,
|
||||
.bit = 1,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_5[] __initconst = {
|
||||
"158LEMS1.103",
|
||||
"158LEMS1.105",
|
||||
"158LEMS1.106",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF5 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_5,
|
||||
.charge_control = {
|
||||
.address = 0xef,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = 0x2f,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = { // todo: reverse
|
||||
.address = 0xbf,
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xf2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_TURBO_NAME, 0xc4 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = { // unsupported?
|
||||
.address = MSI_EC_ADDR_UNKNOWN,
|
||||
.mask = 0x0f,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xf4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68, // needs testing
|
||||
.rt_fan_speed_address = 0x71, // needs testing
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = MSI_EC_ADDR_UNKNOWN,
|
||||
.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = 0x2b,
|
||||
.mute_led_address = 0x2c,
|
||||
.bit = 2,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_6[] __initconst = {
|
||||
"1542EMS1.102",
|
||||
"1542EMS1.104",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF6 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_6,
|
||||
.charge_control = {
|
||||
.address = 0xef,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = MSI_EC_ADDR_UNSUPP,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = 0xbf, // todo: reverse
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xf2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
{ SM_TURBO_NAME, 0xc4 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = {
|
||||
.address = 0xd5,
|
||||
.mask = 0x0f,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xf4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d },
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68,
|
||||
.rt_fan_speed_address = 0xc9,
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = 0x80,
|
||||
.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = MSI_EC_ADDR_UNSUPP,
|
||||
.mute_led_address = MSI_EC_ADDR_UNSUPP,
|
||||
.bit = 2,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const ALLOWED_FW_7[] __initconst = {
|
||||
"17FKEMS1.108",
|
||||
"17FKEMS1.109",
|
||||
"17FKEMS1.10A",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf CONF7 __initdata = {
|
||||
.allowed_fw = ALLOWED_FW_7,
|
||||
.charge_control = {
|
||||
.address = 0xef,
|
||||
.offset_start = 0x8a,
|
||||
.offset_end = 0x80,
|
||||
.range_min = 0x8a,
|
||||
.range_max = 0xe4,
|
||||
},
|
||||
.webcam = {
|
||||
.address = 0x2e,
|
||||
.block_address = MSI_EC_ADDR_UNSUPP,
|
||||
.bit = 1,
|
||||
},
|
||||
.fn_super_swap = {
|
||||
.address = 0xbf, // needs testing
|
||||
.bit = 4,
|
||||
},
|
||||
.cooler_boost = {
|
||||
.address = 0x98,
|
||||
.bit = 7,
|
||||
},
|
||||
.shift_mode = {
|
||||
.address = 0xf2,
|
||||
.modes = {
|
||||
{ SM_ECO_NAME, 0xc2 },
|
||||
{ SM_COMFORT_NAME, 0xc1 },
|
||||
{ SM_SPORT_NAME, 0xc0 },
|
||||
{ SM_TURBO_NAME, 0xc4 },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.super_battery = {
|
||||
.address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes
|
||||
.mask = 0x0f,
|
||||
},
|
||||
.fan_mode = {
|
||||
.address = 0xf4,
|
||||
.modes = {
|
||||
{ FM_AUTO_NAME, 0x0d }, // d may not be relevant
|
||||
{ FM_SILENT_NAME, 0x1d },
|
||||
{ FM_ADVANCED_NAME, 0x8d },
|
||||
MSI_EC_MODE_NULL
|
||||
},
|
||||
},
|
||||
.cpu = {
|
||||
.rt_temp_address = 0x68,
|
||||
.rt_fan_speed_address = 0xc9, // needs testing
|
||||
.rt_fan_speed_base_min = 0x19,
|
||||
.rt_fan_speed_base_max = 0x37,
|
||||
.bs_fan_speed_address = MSI_EC_ADDR_UNSUPP,
|
||||
.bs_fan_speed_base_min = 0x00,
|
||||
.bs_fan_speed_base_max = 0x0f,
|
||||
},
|
||||
.gpu = {
|
||||
.rt_temp_address = MSI_EC_ADDR_UNKNOWN,
|
||||
.rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN,
|
||||
},
|
||||
.leds = {
|
||||
.micmute_led_address = MSI_EC_ADDR_UNSUPP,
|
||||
.mute_led_address = 0x2c,
|
||||
.bit = 2,
|
||||
},
|
||||
.kbd_bl = {
|
||||
.bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ?
|
||||
.bl_modes = { 0x00, 0x08 }, // ?
|
||||
.max_mode = 1, // ?
|
||||
.bl_state_address = 0xf3,
|
||||
.state_base_value = 0x80,
|
||||
.max_state = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static struct msi_ec_conf *CONFIGS[] __initdata = {
|
||||
&CONF0,
|
||||
&CONF1,
|
||||
&CONF2,
|
||||
&CONF3,
|
||||
&CONF4,
|
||||
&CONF5,
|
||||
&CONF6,
|
||||
&CONF7,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct msi_ec_conf conf; // current configuration
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
|
||||
static int ec_read_seq(u8 addr, u8 *buf, u8 len)
|
||||
{
|
||||
int result;
|
||||
|
||||
for (u8 i = 0; i < len; i++) {
|
||||
result = ec_read(addr + i, buf + i);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1])
|
||||
{
|
||||
int result;
|
||||
|
||||
memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1);
|
||||
result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS,
|
||||
buf,
|
||||
MSI_EC_FW_VERSION_LENGTH);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return MSI_EC_FW_VERSION_LENGTH + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs power_supply subsystem
|
||||
*/
|
||||
|
||||
static ssize_t charge_control_threshold_show(u8 offset,
|
||||
struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(conf.charge_control.address, &rdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sysfs_emit(buf, "%i\n", rdata - offset);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_threshold_store(u8 offset,
|
||||
struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
u8 wdata;
|
||||
int result;
|
||||
|
||||
result = kstrtou8(buf, 10, &wdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
wdata += offset;
|
||||
if (wdata < conf.charge_control.range_min ||
|
||||
wdata > conf.charge_control.range_max)
|
||||
return -EINVAL;
|
||||
|
||||
result = ec_write(conf.charge_control.address, wdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t charge_control_start_threshold_show(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return charge_control_threshold_show(conf.charge_control.offset_start,
|
||||
device, attr, buf);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_start_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return charge_control_threshold_store(conf.charge_control.offset_start,
|
||||
dev, attr, buf, count);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_end_threshold_show(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return charge_control_threshold_show(conf.charge_control.offset_end,
|
||||
device, attr, buf);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_end_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return charge_control_threshold_store(conf.charge_control.offset_end,
|
||||
dev, attr, buf, count);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(charge_control_start_threshold);
|
||||
static DEVICE_ATTR_RW(charge_control_end_threshold);
|
||||
|
||||
static struct attribute *msi_battery_attrs[] = {
|
||||
&dev_attr_charge_control_start_threshold.attr,
|
||||
&dev_attr_charge_control_end_threshold.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(msi_battery);
|
||||
|
||||
static int msi_battery_add(struct power_supply *battery,
|
||||
struct acpi_battery_hook *hook)
|
||||
{
|
||||
return device_add_groups(&battery->dev, msi_battery_groups);
|
||||
}
|
||||
|
||||
static int msi_battery_remove(struct power_supply *battery,
|
||||
struct acpi_battery_hook *hook)
|
||||
{
|
||||
device_remove_groups(&battery->dev, msi_battery_groups);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_battery_hook battery_hook = {
|
||||
.add_battery = msi_battery_add,
|
||||
.remove_battery = msi_battery_remove,
|
||||
.name = MSI_EC_DRIVER_NAME,
|
||||
};
|
||||
|
||||
/*
|
||||
* Module load/unload
|
||||
*/
|
||||
|
||||
static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, msi_dmi_table);
|
||||
|
||||
static int __init load_configuration(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1];
|
||||
|
||||
/* get firmware version */
|
||||
result = ec_get_firmware_version(fw_version);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
/* load the suitable configuration, if exists */
|
||||
for (int i = 0; CONFIGS[i]; i++) {
|
||||
if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) {
|
||||
conf = *CONFIGS[i];
|
||||
conf.allowed_fw = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* config not found */
|
||||
|
||||
for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) {
|
||||
if (!isgraph(fw_version[i])) {
|
||||
pr_warn("Unable to find a valid firmware version!\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
pr_warn("Firmware version is not supported: '%s'\n", fw_version);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int __init msi_ec_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = load_configuration();
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
battery_hook_register(&battery_hook);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit msi_ec_exit(void)
|
||||
{
|
||||
battery_hook_unregister(&battery_hook);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>");
|
||||
MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>");
|
||||
MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>");
|
||||
MODULE_DESCRIPTION("MSI Embedded Controller");
|
||||
|
||||
module_init(msi_ec_init);
|
||||
module_exit(msi_ec_exit);
|
122
drivers/platform/x86/msi-ec.h
Normal file
122
drivers/platform/x86/msi-ec.h
Normal file
@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/*
|
||||
* msi-ec: MSI laptops' embedded controller driver.
|
||||
*
|
||||
* Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es>
|
||||
* Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev>
|
||||
* Copyright (C) 2023 Nikita Kravets <teackot@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef _MSI_EC_H_
|
||||
#define _MSI_EC_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MSI_EC_DRIVER_NAME "msi-ec"
|
||||
|
||||
#define MSI_EC_ADDR_UNKNOWN 0xff01 // unknown address
|
||||
#define MSI_EC_ADDR_UNSUPP 0xff01 // unsupported parameter
|
||||
|
||||
// Firmware info addresses are universal
|
||||
#define MSI_EC_FW_VERSION_ADDRESS 0xa0
|
||||
#define MSI_EC_FW_DATE_ADDRESS 0xac
|
||||
#define MSI_EC_FW_TIME_ADDRESS 0xb4
|
||||
#define MSI_EC_FW_VERSION_LENGTH 12
|
||||
#define MSI_EC_FW_DATE_LENGTH 8
|
||||
#define MSI_EC_FW_TIME_LENGTH 8
|
||||
|
||||
struct msi_ec_charge_control_conf {
|
||||
int address;
|
||||
int offset_start;
|
||||
int offset_end;
|
||||
int range_min;
|
||||
int range_max;
|
||||
};
|
||||
|
||||
struct msi_ec_webcam_conf {
|
||||
int address;
|
||||
int block_address;
|
||||
int bit;
|
||||
};
|
||||
|
||||
struct msi_ec_fn_super_swap_conf {
|
||||
int address;
|
||||
int bit;
|
||||
};
|
||||
|
||||
struct msi_ec_cooler_boost_conf {
|
||||
int address;
|
||||
int bit;
|
||||
};
|
||||
|
||||
#define MSI_EC_MODE_NULL { NULL, 0 }
|
||||
struct msi_ec_mode {
|
||||
const char *name;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct msi_ec_shift_mode_conf {
|
||||
int address;
|
||||
struct msi_ec_mode modes[5]; // fixed size for easier hard coding
|
||||
};
|
||||
|
||||
struct msi_ec_super_battery_conf {
|
||||
int address;
|
||||
int mask;
|
||||
};
|
||||
|
||||
struct msi_ec_fan_mode_conf {
|
||||
int address;
|
||||
struct msi_ec_mode modes[5]; // fixed size for easier hard coding
|
||||
};
|
||||
|
||||
struct msi_ec_cpu_conf {
|
||||
int rt_temp_address;
|
||||
int rt_fan_speed_address; // realtime
|
||||
int rt_fan_speed_base_min;
|
||||
int rt_fan_speed_base_max;
|
||||
int bs_fan_speed_address; // basic
|
||||
int bs_fan_speed_base_min;
|
||||
int bs_fan_speed_base_max;
|
||||
};
|
||||
|
||||
struct msi_ec_gpu_conf {
|
||||
int rt_temp_address;
|
||||
int rt_fan_speed_address; // realtime
|
||||
};
|
||||
|
||||
struct msi_ec_led_conf {
|
||||
int micmute_led_address;
|
||||
int mute_led_address;
|
||||
int bit;
|
||||
};
|
||||
|
||||
#define MSI_EC_KBD_BL_STATE_MASK 0x3
|
||||
struct msi_ec_kbd_bl_conf {
|
||||
int bl_mode_address;
|
||||
int bl_modes[2];
|
||||
int max_mode;
|
||||
|
||||
int bl_state_address;
|
||||
int state_base_value;
|
||||
int max_state;
|
||||
};
|
||||
|
||||
struct msi_ec_conf {
|
||||
const char * const *allowed_fw;
|
||||
|
||||
struct msi_ec_charge_control_conf charge_control;
|
||||
struct msi_ec_webcam_conf webcam;
|
||||
struct msi_ec_fn_super_swap_conf fn_super_swap;
|
||||
struct msi_ec_cooler_boost_conf cooler_boost;
|
||||
struct msi_ec_shift_mode_conf shift_mode;
|
||||
struct msi_ec_super_battery_conf super_battery;
|
||||
struct msi_ec_fan_mode_conf fan_mode;
|
||||
struct msi_ec_cpu_conf cpu;
|
||||
struct msi_ec_gpu_conf gpu;
|
||||
struct msi_ec_led_conf leds;
|
||||
struct msi_ec_kbd_bl_conf kbd_bl;
|
||||
};
|
||||
|
||||
#endif // _MSI_EC_H_
|
@ -291,5 +291,4 @@ MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
|
||||
MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
|
||||
MODULE_ALIAS("platform:pcengines-apuv2");
|
||||
MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");
|
||||
|
@ -1,128 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* PEAQ 2-in-1 WMI hotkey driver
|
||||
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000"
|
||||
#define PEAQ_DOLBY_BUTTON_METHOD_ID 5
|
||||
#define PEAQ_POLL_INTERVAL_MS 250
|
||||
#define PEAQ_POLL_IGNORE_MS 500
|
||||
#define PEAQ_POLL_MAX_MS 1000
|
||||
|
||||
MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID);
|
||||
|
||||
static struct input_dev *peaq_poll_dev;
|
||||
|
||||
/*
|
||||
* The Dolby button (yes really a Dolby button) causes an ACPI variable to get
|
||||
* set on both press and release. The WMI method checks and clears that flag.
|
||||
* So for a press + release we will get back One from the WMI method either once
|
||||
* (if polling after the release) or twice (polling between press and release).
|
||||
* We ignore events for 0.5s after the first event to avoid reporting 2 presses.
|
||||
*/
|
||||
static void peaq_wmi_poll(struct input_dev *input_dev)
|
||||
{
|
||||
static unsigned long last_event_time;
|
||||
static bool had_events;
|
||||
union acpi_object obj;
|
||||
acpi_status status;
|
||||
u32 dummy = 0;
|
||||
|
||||
struct acpi_buffer input = { sizeof(dummy), &dummy };
|
||||
struct acpi_buffer output = { sizeof(obj), &obj };
|
||||
|
||||
status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 0,
|
||||
PEAQ_DOLBY_BUTTON_METHOD_ID,
|
||||
&input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
if (obj.type != ACPI_TYPE_INTEGER) {
|
||||
dev_err(&input_dev->dev,
|
||||
"Error WMBC did not return an integer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj.integer.value)
|
||||
return;
|
||||
|
||||
if (had_events && time_before(jiffies, last_event_time +
|
||||
msecs_to_jiffies(PEAQ_POLL_IGNORE_MS)))
|
||||
return;
|
||||
|
||||
input_event(input_dev, EV_KEY, KEY_SOUND, 1);
|
||||
input_sync(input_dev);
|
||||
input_event(input_dev, EV_KEY, KEY_SOUND, 0);
|
||||
input_sync(input_dev);
|
||||
|
||||
last_event_time = jiffies;
|
||||
had_events = true;
|
||||
}
|
||||
|
||||
/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */
|
||||
static const struct dmi_system_id peaq_dmi_table[] __initconst = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init peaq_wmi_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* WMI GUID is not unique, also check for a DMI match */
|
||||
if (!dmi_check_system(peaq_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
|
||||
return -ENODEV;
|
||||
|
||||
peaq_poll_dev = input_allocate_device();
|
||||
if (!peaq_poll_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
peaq_poll_dev->name = "PEAQ WMI hotkeys";
|
||||
peaq_poll_dev->phys = "wmi/input0";
|
||||
peaq_poll_dev->id.bustype = BUS_HOST;
|
||||
input_set_capability(peaq_poll_dev, EV_KEY, KEY_SOUND);
|
||||
|
||||
err = input_setup_polling(peaq_poll_dev, peaq_wmi_poll);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
input_set_poll_interval(peaq_poll_dev, PEAQ_POLL_INTERVAL_MS);
|
||||
input_set_max_poll_interval(peaq_poll_dev, PEAQ_POLL_MAX_MS);
|
||||
|
||||
err = input_register_device(peaq_poll_dev);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
input_free_device(peaq_poll_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit peaq_wmi_exit(void)
|
||||
{
|
||||
input_unregister_device(peaq_poll_dev);
|
||||
}
|
||||
|
||||
module_init(peaq_wmi_init);
|
||||
module_exit(peaq_wmi_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver");
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -65,14 +65,12 @@ static int samsungq10_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int samsungq10_remove(struct platform_device *pdev)
|
||||
static void samsungq10_remove(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
struct backlight_device *bd = platform_get_drvdata(pdev);
|
||||
|
||||
backlight_device_unregister(bd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver samsungq10_driver = {
|
||||
@ -80,7 +78,7 @@ static struct platform_driver samsungq10_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = samsungq10_probe,
|
||||
.remove = samsungq10_remove,
|
||||
.remove_new = samsungq10_remove,
|
||||
};
|
||||
|
||||
static struct platform_device *samsungq10_device;
|
||||
|
@ -265,13 +265,11 @@ static int smi_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
static int smi_remove(struct platform_device *pdev)
|
||||
static void smi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct smi *smi = platform_get_drvdata(pdev);
|
||||
|
||||
smi_devs_unregister(smi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct smi_node bsg1160_data = {
|
||||
@ -339,7 +337,7 @@ static struct platform_driver smi_driver = {
|
||||
.acpi_match_table = smi_acpi_ids,
|
||||
},
|
||||
.probe = smi_probe,
|
||||
.remove = smi_remove,
|
||||
.remove_new = smi_remove,
|
||||
};
|
||||
module_platform_driver(smi_driver);
|
||||
|
||||
|
@ -3287,7 +3287,7 @@ static void sony_nc_remove(struct acpi_device *device)
|
||||
dprintk(SONY_NC_DRIVER_NAME " removed.\n");
|
||||
}
|
||||
|
||||
static const struct acpi_device_id sony_device_ids[] = {
|
||||
static const struct acpi_device_id sony_device_ids[] __maybe_unused = {
|
||||
{SONY_NC_HID, 0},
|
||||
{SONY_PIC_HID, 0},
|
||||
{"", 0},
|
||||
|
@ -862,19 +862,18 @@ static umode_t auth_attr_is_visible(struct kobject *kobj,
|
||||
struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
|
||||
|
||||
/* We only want to display level and index settings on HDD/NVMe */
|
||||
if ((attr == (struct attribute *)&auth_index) ||
|
||||
(attr == (struct attribute *)&auth_level)) {
|
||||
if (attr == &auth_index.attr || attr == &auth_level.attr) {
|
||||
if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme))
|
||||
return attr->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only display certificates on Admin account, if supported */
|
||||
if ((attr == (struct attribute *)&auth_certificate) ||
|
||||
(attr == (struct attribute *)&auth_signature) ||
|
||||
(attr == (struct attribute *)&auth_save_signature) ||
|
||||
(attr == (struct attribute *)&auth_cert_thumb) ||
|
||||
(attr == (struct attribute *)&auth_cert_to_password)) {
|
||||
if (attr == &auth_certificate.attr ||
|
||||
attr == &auth_signature.attr ||
|
||||
attr == &auth_save_signature.attr ||
|
||||
attr == &auth_cert_thumb.attr ||
|
||||
attr == &auth_cert_to_password.attr) {
|
||||
if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support)
|
||||
return attr->mode;
|
||||
return 0;
|
||||
@ -1079,33 +1078,6 @@ static const struct attribute_group tlmi_attr_group = {
|
||||
.attrs = tlmi_attrs,
|
||||
};
|
||||
|
||||
static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct kobj_attribute *kattr;
|
||||
|
||||
kattr = container_of(attr, struct kobj_attribute, attr);
|
||||
if (kattr->show)
|
||||
return kattr->show(kobj, kattr, buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct kobj_attribute *kattr;
|
||||
|
||||
kattr = container_of(attr, struct kobj_attribute, attr);
|
||||
if (kattr->store)
|
||||
return kattr->store(kobj, kattr, buf, count);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static const struct sysfs_ops tlmi_kobj_sysfs_ops = {
|
||||
.show = tlmi_attr_show,
|
||||
.store = tlmi_attr_store,
|
||||
};
|
||||
|
||||
static void tlmi_attr_setting_release(struct kobject *kobj)
|
||||
{
|
||||
struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
|
||||
@ -1123,12 +1095,12 @@ static void tlmi_pwd_setting_release(struct kobject *kobj)
|
||||
|
||||
static const struct kobj_type tlmi_attr_setting_ktype = {
|
||||
.release = &tlmi_attr_setting_release,
|
||||
.sysfs_ops = &tlmi_kobj_sysfs_ops,
|
||||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
};
|
||||
|
||||
static const struct kobj_type tlmi_pwd_setting_ktype = {
|
||||
.release = &tlmi_pwd_setting_release,
|
||||
.sysfs_ops = &tlmi_kobj_sysfs_ops,
|
||||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
};
|
||||
|
||||
static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
@ -1385,7 +1357,6 @@ static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type,
|
||||
|
||||
static int tlmi_analyze(void)
|
||||
{
|
||||
acpi_status status;
|
||||
int i, ret;
|
||||
|
||||
if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) &&
|
||||
@ -1422,8 +1393,8 @@ static int tlmi_analyze(void)
|
||||
char *p;
|
||||
|
||||
tlmi_priv.setting[i] = NULL;
|
||||
status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID);
|
||||
if (ACPI_FAILURE(status))
|
||||
ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID);
|
||||
if (ret)
|
||||
break;
|
||||
if (!item)
|
||||
break;
|
||||
|
@ -11699,6 +11699,7 @@ static int __init thinkpad_acpi_module_init(void)
|
||||
{
|
||||
const struct dmi_system_id *dmi_id;
|
||||
int ret, i;
|
||||
acpi_object_type obj_type;
|
||||
|
||||
tpacpi_lifecycle = TPACPI_LIFE_INIT;
|
||||
|
||||
@ -11724,6 +11725,21 @@ static int __init thinkpad_acpi_module_init(void)
|
||||
TPACPI_ACPIHANDLE_INIT(ecrd);
|
||||
TPACPI_ACPIHANDLE_INIT(ecwr);
|
||||
|
||||
/*
|
||||
* Quirk: in some models (e.g. X380 Yoga), an object named ECRD
|
||||
* exists, but it is a register, not a method.
|
||||
*/
|
||||
if (ecrd_handle) {
|
||||
acpi_get_type(ecrd_handle, &obj_type);
|
||||
if (obj_type != ACPI_TYPE_METHOD)
|
||||
ecrd_handle = NULL;
|
||||
}
|
||||
if (ecwr_handle) {
|
||||
acpi_get_type(ecwr_handle, &obj_type);
|
||||
if (obj_type != ACPI_TYPE_METHOD)
|
||||
ecwr_handle = NULL;
|
||||
}
|
||||
|
||||
tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME);
|
||||
if (!tpacpi_wq) {
|
||||
thinkpad_acpi_module_exit();
|
||||
|
@ -1369,7 +1369,7 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
|
||||
event, 0);
|
||||
}
|
||||
|
||||
static int acpi_wmi_remove(struct platform_device *device)
|
||||
static void acpi_wmi_remove(struct platform_device *device)
|
||||
{
|
||||
struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev);
|
||||
|
||||
@ -1379,8 +1379,6 @@ static int acpi_wmi_remove(struct platform_device *device)
|
||||
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
|
||||
wmi_free_devices(acpi_device);
|
||||
device_unregister(dev_get_drvdata(&device->dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_wmi_probe(struct platform_device *device)
|
||||
@ -1468,7 +1466,7 @@ static struct platform_driver acpi_wmi_driver = {
|
||||
.acpi_match_table = wmi_device_ids,
|
||||
},
|
||||
.probe = acpi_wmi_probe,
|
||||
.remove = acpi_wmi_remove,
|
||||
.remove_new = acpi_wmi_remove,
|
||||
};
|
||||
|
||||
static int __init acpi_wmi_init(void)
|
||||
|
File diff suppressed because it is too large
Load Diff
21
drivers/platform/x86/x86-android-tablets/Kconfig
Normal file
21
drivers/platform/x86/x86-android-tablets/Kconfig
Normal file
@ -0,0 +1,21 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# X86 Android tablet support Kconfig
|
||||
#
|
||||
|
||||
config X86_ANDROID_TABLETS
|
||||
tristate "X86 Android tablet support"
|
||||
depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB && PMIC_OPREGION
|
||||
help
|
||||
X86 tablets which ship with Android as (part of) the factory image
|
||||
typically have various problems with their DSDTs. The factory kernels
|
||||
shipped on these devices typically have device addresses and GPIOs
|
||||
hardcoded in the kernel, rather than specified in their DSDT.
|
||||
|
||||
With the DSDT containing a random collection of devices which may or
|
||||
may not actually be present. This driver contains various fixes for
|
||||
such tablets, including instantiating kernel devices for devices which
|
||||
are missing from the DSDT.
|
||||
|
||||
If you have a x86 Android tablet say Y or M here, for a generic x86
|
||||
distro config say M here.
|
9
drivers/platform/x86/x86-android-tablets/Makefile
Normal file
9
drivers/platform/x86/x86-android-tablets/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# X86 Android tablet support Makefile
|
||||
#
|
||||
|
||||
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
|
||||
|
||||
x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
|
||||
asus.o lenovo.o other.o
|
325
drivers/platform/x86/x86-android-tablets/asus.c
Normal file
325
drivers/platform/x86/x86-android-tablets/asus.c
Normal file
@ -0,0 +1,325 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Board info for Asus X86 tablets which ship with Android as the factory image
|
||||
* and which have broken DSDT tables. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "shared-psy-info.h"
|
||||
#include "x86-android-tablets.h"
|
||||
|
||||
/* Asus ME176C and TF103C tablets shared data */
|
||||
static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = {
|
||||
.dev_id = "intel-int3496",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct x86_gpio_button asus_me176c_tf103c_lid = {
|
||||
.button = {
|
||||
.code = SW_LID,
|
||||
.active_low = true,
|
||||
.desc = "lid_sw",
|
||||
.type = EV_SW,
|
||||
.wakeup = true,
|
||||
.debounce_interval = 50,
|
||||
},
|
||||
.chip = "INT33FC:02",
|
||||
.pin = 12,
|
||||
};
|
||||
|
||||
/* Asus ME176C tablets have an Android factory img with everything hardcoded */
|
||||
static const char * const asus_me176c_accel_mount_matrix[] = {
|
||||
"-1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry asus_me176c_accel_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_me176c_accel_node = {
|
||||
.properties = asus_me176c_accel_props,
|
||||
};
|
||||
|
||||
static const struct property_entry asus_me176c_bq24190_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
|
||||
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node),
|
||||
PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000),
|
||||
PROPERTY_ENTRY_BOOL("omit-battery-class"),
|
||||
PROPERTY_ENTRY_BOOL("disable-reset"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_me176c_bq24190_node = {
|
||||
.properties = asus_me176c_bq24190_props,
|
||||
};
|
||||
|
||||
static const struct property_entry asus_me176c_ug3105_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1),
|
||||
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node),
|
||||
PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_me176c_ug3105_node = {
|
||||
.properties = asus_me176c_ug3105_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* bq24297 battery charger */
|
||||
.board_info = {
|
||||
.type = "bq24190",
|
||||
.addr = 0x6b,
|
||||
.dev_name = "bq24297",
|
||||
.swnode = &asus_me176c_bq24190_node,
|
||||
.platform_data = &bq24190_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C1",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_PMIC,
|
||||
.chip = "\\_SB_.I2C7.PMIC",
|
||||
.domain = DOMAIN_BUS_WAKEUP,
|
||||
.index = 0,
|
||||
},
|
||||
}, {
|
||||
/* ug3105 battery monitor */
|
||||
.board_info = {
|
||||
.type = "ug3105",
|
||||
.addr = 0x70,
|
||||
.dev_name = "ug3105",
|
||||
.swnode = &asus_me176c_ug3105_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C1",
|
||||
}, {
|
||||
/* ak09911 compass */
|
||||
.board_info = {
|
||||
.type = "ak09911",
|
||||
.addr = 0x0c,
|
||||
.dev_name = "ak09911",
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
}, {
|
||||
/* kxtj21009 accel */
|
||||
.board_info = {
|
||||
.type = "kxtj21009",
|
||||
.addr = 0x0f,
|
||||
.dev_name = "kxtj21009",
|
||||
.swnode = &asus_me176c_accel_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_APIC,
|
||||
.index = 0x44,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
}, {
|
||||
/* goodix touchscreen */
|
||||
.board_info = {
|
||||
.type = "GDIX1001:00",
|
||||
.addr = 0x14,
|
||||
.dev_name = "goodix_ts",
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C6",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_APIC,
|
||||
.index = 0x45,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = {
|
||||
{
|
||||
.ctrl_hid = "80860F0A",
|
||||
.ctrl_uid = "2",
|
||||
.ctrl_devname = "serial0",
|
||||
.serdev_hid = "BCM2E3A",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table asus_me176c_goodix_gpios = {
|
||||
.dev_id = "i2c-goodix_ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const asus_me176c_gpios[] = {
|
||||
&int3496_gpo2_pin22_gpios,
|
||||
&asus_me176c_goodix_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info asus_me176c_info __initconst = {
|
||||
.i2c_client_info = asus_me176c_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients),
|
||||
.pdev_info = int3496_pdevs,
|
||||
.pdev_count = 1,
|
||||
.serdev_info = asus_me176c_serdevs,
|
||||
.serdev_count = ARRAY_SIZE(asus_me176c_serdevs),
|
||||
.gpio_button = &asus_me176c_tf103c_lid,
|
||||
.gpiod_lookup_tables = asus_me176c_gpios,
|
||||
.bat_swnode = &generic_lipo_hv_4v35_battery_node,
|
||||
.modules = bq24190_modules,
|
||||
.invalid_aei_gpiochip = "INT33FC:02",
|
||||
};
|
||||
|
||||
/* Asus TF103C tablets have an Android factory img with everything hardcoded */
|
||||
static const char * const asus_tf103c_accel_mount_matrix[] = {
|
||||
"0", "-1", "0",
|
||||
"-1", "0", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry asus_tf103c_accel_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_tf103c_accel_node = {
|
||||
.properties = asus_tf103c_accel_props,
|
||||
};
|
||||
|
||||
static const struct property_entry asus_tf103c_touchscreen_props[] = {
|
||||
PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_tf103c_touchscreen_node = {
|
||||
.properties = asus_tf103c_touchscreen_props,
|
||||
};
|
||||
|
||||
static const struct property_entry asus_tf103c_battery_props[] = {
|
||||
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
||||
PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"),
|
||||
PROPERTY_ENTRY_U32("precharge-current-microamp", 256000),
|
||||
PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000),
|
||||
PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000),
|
||||
PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000),
|
||||
PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_tf103c_battery_node = {
|
||||
.properties = asus_tf103c_battery_props,
|
||||
};
|
||||
|
||||
static const struct property_entry asus_tf103c_bq24190_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
|
||||
PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node),
|
||||
PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000),
|
||||
PROPERTY_ENTRY_BOOL("omit-battery-class"),
|
||||
PROPERTY_ENTRY_BOOL("disable-reset"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_tf103c_bq24190_node = {
|
||||
.properties = asus_tf103c_bq24190_props,
|
||||
};
|
||||
|
||||
static const struct property_entry asus_tf103c_ug3105_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1),
|
||||
PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node),
|
||||
PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node asus_tf103c_ug3105_node = {
|
||||
.properties = asus_tf103c_ug3105_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* bq24297 battery charger */
|
||||
.board_info = {
|
||||
.type = "bq24190",
|
||||
.addr = 0x6b,
|
||||
.dev_name = "bq24297",
|
||||
.swnode = &asus_tf103c_bq24190_node,
|
||||
.platform_data = &bq24190_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C1",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_PMIC,
|
||||
.chip = "\\_SB_.I2C7.PMIC",
|
||||
.domain = DOMAIN_BUS_WAKEUP,
|
||||
.index = 0,
|
||||
},
|
||||
}, {
|
||||
/* ug3105 battery monitor */
|
||||
.board_info = {
|
||||
.type = "ug3105",
|
||||
.addr = 0x70,
|
||||
.dev_name = "ug3105",
|
||||
.swnode = &asus_tf103c_ug3105_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C1",
|
||||
}, {
|
||||
/* ak09911 compass */
|
||||
.board_info = {
|
||||
.type = "ak09911",
|
||||
.addr = 0x0c,
|
||||
.dev_name = "ak09911",
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
}, {
|
||||
/* kxtj21009 accel */
|
||||
.board_info = {
|
||||
.type = "kxtj21009",
|
||||
.addr = 0x0f,
|
||||
.dev_name = "kxtj21009",
|
||||
.swnode = &asus_tf103c_accel_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
}, {
|
||||
/* atmel touchscreen */
|
||||
.board_info = {
|
||||
.type = "atmel_mxt_ts",
|
||||
.addr = 0x4a,
|
||||
.dev_name = "atmel_mxt_ts",
|
||||
.swnode = &asus_tf103c_touchscreen_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C6",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 28,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const asus_tf103c_gpios[] = {
|
||||
&int3496_gpo2_pin22_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info asus_tf103c_info __initconst = {
|
||||
.i2c_client_info = asus_tf103c_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients),
|
||||
.pdev_info = int3496_pdevs,
|
||||
.pdev_count = 1,
|
||||
.gpio_button = &asus_me176c_tf103c_lid,
|
||||
.gpiod_lookup_tables = asus_tf103c_gpios,
|
||||
.bat_swnode = &asus_tf103c_battery_node,
|
||||
.modules = bq24190_modules,
|
||||
.invalid_aei_gpiochip = "INT33FC:02",
|
||||
};
|
391
drivers/platform/x86/x86-android-tablets/core.c
Normal file
391
drivers/platform/x86/x86-android-tablets/core.c
Normal file
@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* DMI based code to deal with broken DSDTs on X86 tablets which ship with
|
||||
* Android as (part of) the factory image. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "x86-android-tablets.h"
|
||||
/* For gpiochip_get_desc() which is EXPORT_SYMBOL_GPL() */
|
||||
#include "../../../gpio/gpiolib.h"
|
||||
#include "../../../gpio/gpiolib-acpi.h"
|
||||
|
||||
static int gpiochip_find_match_label(struct gpio_chip *gc, void *data)
|
||||
{
|
||||
return gc->label && !strcmp(gc->label, data);
|
||||
}
|
||||
|
||||
int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc)
|
||||
{
|
||||
struct gpio_desc *gpiod;
|
||||
struct gpio_chip *chip;
|
||||
|
||||
chip = gpiochip_find((void *)label, gpiochip_find_match_label);
|
||||
if (!chip) {
|
||||
pr_err("error cannot find GPIO chip %s\n", label);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpiod = gpiochip_get_desc(chip, pin);
|
||||
if (IS_ERR(gpiod)) {
|
||||
pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin);
|
||||
return PTR_ERR(gpiod);
|
||||
}
|
||||
|
||||
*desc = gpiod;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data)
|
||||
{
|
||||
struct irq_fwspec fwspec = { };
|
||||
struct irq_domain *domain;
|
||||
struct acpi_device *adev;
|
||||
struct gpio_desc *gpiod;
|
||||
unsigned int irq_type;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
int irq, ret;
|
||||
|
||||
switch (data->type) {
|
||||
case X86_ACPI_IRQ_TYPE_APIC:
|
||||
/*
|
||||
* The DSDT may already reference the GSI in a device skipped by
|
||||
* acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI
|
||||
* to avoid EBUSY errors in this case.
|
||||
*/
|
||||
acpi_unregister_gsi(data->index);
|
||||
irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity);
|
||||
if (irq < 0)
|
||||
pr_err("error %d getting APIC IRQ %d\n", irq, data->index);
|
||||
|
||||
return irq;
|
||||
case X86_ACPI_IRQ_TYPE_GPIOINT:
|
||||
/* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */
|
||||
ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq = gpiod_to_irq(gpiod);
|
||||
if (irq < 0) {
|
||||
pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index);
|
||||
return irq;
|
||||
}
|
||||
|
||||
irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity);
|
||||
if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq))
|
||||
irq_set_irq_type(irq, irq_type);
|
||||
|
||||
return irq;
|
||||
case X86_ACPI_IRQ_TYPE_PMIC:
|
||||
status = acpi_get_handle(NULL, data->chip, &handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("error could not get %s handle\n", data->chip);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adev = acpi_fetch_acpi_dev(handle);
|
||||
if (!adev) {
|
||||
pr_err("error could not get %s adev\n", data->chip);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
fwspec.fwnode = acpi_fwnode_handle(adev);
|
||||
domain = irq_find_matching_fwspec(&fwspec, data->domain);
|
||||
if (!domain) {
|
||||
pr_err("error could not find IRQ domain for %s\n", data->chip);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return irq_create_mapping(domain, data->index);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int i2c_client_count;
|
||||
static int pdev_count;
|
||||
static int serdev_count;
|
||||
static struct i2c_client **i2c_clients;
|
||||
static struct platform_device **pdevs;
|
||||
static struct serdev_device **serdevs;
|
||||
static struct gpiod_lookup_table * const *gpiod_lookup_tables;
|
||||
static const struct software_node *bat_swnode;
|
||||
static void (*exit_handler)(void);
|
||||
|
||||
static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info,
|
||||
int idx)
|
||||
{
|
||||
const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx];
|
||||
struct i2c_board_info board_info = client_info->board_info;
|
||||
struct i2c_adapter *adap;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
|
||||
board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data);
|
||||
if (board_info.irq < 0)
|
||||
return board_info.irq;
|
||||
|
||||
status = acpi_get_handle(NULL, client_info->adapter_path, &handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Error could not get %s handle\n", client_info->adapter_path);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adap = i2c_acpi_find_adapter_by_handle(handle);
|
||||
if (!adap) {
|
||||
pr_err("error could not get %s adapter\n", client_info->adapter_path);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
i2c_clients[idx] = i2c_new_client_device(adap, &board_info);
|
||||
put_device(&adap->dev);
|
||||
if (IS_ERR(i2c_clients[idx]))
|
||||
return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]),
|
||||
"creating I2C-client %d\n", idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx)
|
||||
{
|
||||
struct acpi_device *ctrl_adev, *serdev_adev;
|
||||
struct serdev_device *serdev;
|
||||
struct device *ctrl_dev;
|
||||
int ret = -ENODEV;
|
||||
|
||||
ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1);
|
||||
if (!ctrl_adev) {
|
||||
pr_err("error could not get %s/%s ctrl adev\n",
|
||||
info->ctrl_hid, info->ctrl_uid);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1);
|
||||
if (!serdev_adev) {
|
||||
pr_err("error could not get %s serdev adev\n", info->serdev_hid);
|
||||
goto put_ctrl_adev;
|
||||
}
|
||||
|
||||
/* get_first_physical_node() returns a weak ref, no need to put() it */
|
||||
ctrl_dev = acpi_get_first_physical_node(ctrl_adev);
|
||||
if (!ctrl_dev) {
|
||||
pr_err("error could not get %s/%s ctrl physical dev\n",
|
||||
info->ctrl_hid, info->ctrl_uid);
|
||||
goto put_serdev_adev;
|
||||
}
|
||||
|
||||
/* ctrl_dev now points to the controller's parent, get the controller */
|
||||
ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname);
|
||||
if (!ctrl_dev) {
|
||||
pr_err("error could not get %s/%s %s ctrl dev\n",
|
||||
info->ctrl_hid, info->ctrl_uid, info->ctrl_devname);
|
||||
goto put_serdev_adev;
|
||||
}
|
||||
|
||||
serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
|
||||
if (!serdev) {
|
||||
ret = -ENOMEM;
|
||||
goto put_serdev_adev;
|
||||
}
|
||||
|
||||
ACPI_COMPANION_SET(&serdev->dev, serdev_adev);
|
||||
acpi_device_set_enumerated(serdev_adev);
|
||||
|
||||
ret = serdev_device_add(serdev);
|
||||
if (ret) {
|
||||
dev_err(&serdev->dev, "error %d adding serdev\n", ret);
|
||||
serdev_device_put(serdev);
|
||||
goto put_serdev_adev;
|
||||
}
|
||||
|
||||
serdevs[idx] = serdev;
|
||||
|
||||
put_serdev_adev:
|
||||
acpi_dev_put(serdev_adev);
|
||||
put_ctrl_adev:
|
||||
acpi_dev_put(ctrl_adev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void x86_android_tablet_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < serdev_count; i++) {
|
||||
if (serdevs[i])
|
||||
serdev_device_remove(serdevs[i]);
|
||||
}
|
||||
|
||||
kfree(serdevs);
|
||||
|
||||
for (i = 0; i < pdev_count; i++)
|
||||
platform_device_unregister(pdevs[i]);
|
||||
|
||||
kfree(pdevs);
|
||||
|
||||
for (i = 0; i < i2c_client_count; i++)
|
||||
i2c_unregister_device(i2c_clients[i]);
|
||||
|
||||
kfree(i2c_clients);
|
||||
|
||||
if (exit_handler)
|
||||
exit_handler();
|
||||
|
||||
for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
|
||||
gpiod_remove_lookup_table(gpiod_lookup_tables[i]);
|
||||
|
||||
software_node_unregister(bat_swnode);
|
||||
}
|
||||
|
||||
static __init int x86_android_tablet_init(void)
|
||||
{
|
||||
const struct x86_dev_info *dev_info;
|
||||
const struct dmi_system_id *id;
|
||||
struct gpio_chip *chip;
|
||||
int i, ret = 0;
|
||||
|
||||
id = dmi_first_match(x86_android_tablet_ids);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
dev_info = id->driver_data;
|
||||
|
||||
/*
|
||||
* The broken DSDTs on these devices often also include broken
|
||||
* _AEI (ACPI Event Interrupt) handlers, disable these.
|
||||
*/
|
||||
if (dev_info->invalid_aei_gpiochip) {
|
||||
chip = gpiochip_find(dev_info->invalid_aei_gpiochip,
|
||||
gpiochip_find_match_label);
|
||||
if (!chip) {
|
||||
pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip);
|
||||
return -ENODEV;
|
||||
}
|
||||
acpi_gpiochip_free_interrupts(chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since this runs from module_init() it cannot use -EPROBE_DEFER,
|
||||
* instead pre-load any modules which are listed as requirements.
|
||||
*/
|
||||
for (i = 0; dev_info->modules && dev_info->modules[i]; i++)
|
||||
request_module(dev_info->modules[i]);
|
||||
|
||||
bat_swnode = dev_info->bat_swnode;
|
||||
if (bat_swnode) {
|
||||
ret = software_node_register(bat_swnode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpiod_lookup_tables = dev_info->gpiod_lookup_tables;
|
||||
for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
|
||||
gpiod_add_lookup_table(gpiod_lookup_tables[i]);
|
||||
|
||||
if (dev_info->init) {
|
||||
ret = dev_info->init();
|
||||
if (ret < 0) {
|
||||
x86_android_tablet_cleanup();
|
||||
return ret;
|
||||
}
|
||||
exit_handler = dev_info->exit;
|
||||
}
|
||||
|
||||
i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL);
|
||||
if (!i2c_clients) {
|
||||
x86_android_tablet_cleanup();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_client_count = dev_info->i2c_client_count;
|
||||
for (i = 0; i < i2c_client_count; i++) {
|
||||
ret = x86_instantiate_i2c_client(dev_info, i);
|
||||
if (ret < 0) {
|
||||
x86_android_tablet_cleanup();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* + 1 to make space for (optional) gpio_keys_button pdev */
|
||||
pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL);
|
||||
if (!pdevs) {
|
||||
x86_android_tablet_cleanup();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pdev_count = dev_info->pdev_count;
|
||||
for (i = 0; i < pdev_count; i++) {
|
||||
pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]);
|
||||
if (IS_ERR(pdevs[i])) {
|
||||
x86_android_tablet_cleanup();
|
||||
return PTR_ERR(pdevs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL);
|
||||
if (!serdevs) {
|
||||
x86_android_tablet_cleanup();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
serdev_count = dev_info->serdev_count;
|
||||
for (i = 0; i < serdev_count; i++) {
|
||||
ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i);
|
||||
if (ret < 0) {
|
||||
x86_android_tablet_cleanup();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev_info->gpio_button) {
|
||||
struct gpio_keys_platform_data pdata = {
|
||||
.buttons = &dev_info->gpio_button->button,
|
||||
.nbuttons = 1,
|
||||
};
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
/* Get GPIO for the gpio-button */
|
||||
ret = x86_android_tablet_get_gpiod(dev_info->gpio_button->chip,
|
||||
dev_info->gpio_button->pin, &gpiod);
|
||||
if (ret < 0) {
|
||||
x86_android_tablet_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info->gpio_button->button.gpio = desc_to_gpio(gpiod);
|
||||
|
||||
pdevs[pdev_count] = platform_device_register_data(NULL, "gpio-keys",
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&pdata, sizeof(pdata));
|
||||
if (IS_ERR(pdevs[pdev_count])) {
|
||||
x86_android_tablet_cleanup();
|
||||
return PTR_ERR(pdevs[pdev_count]);
|
||||
}
|
||||
pdev_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(x86_android_tablet_init);
|
||||
module_exit(x86_android_tablet_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver");
|
||||
MODULE_LICENSE("GPL");
|
165
drivers/platform/x86/x86-android-tablets/dmi.c
Normal file
165
drivers/platform/x86/x86-android-tablets/dmi.c
Normal file
@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* DMI based code to deal with broken DSDTs on X86 tablets which ship with
|
||||
* Android as (part of) the factory image. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "x86-android-tablets.h"
|
||||
|
||||
const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
|
||||
{
|
||||
/* Acer Iconia One 7 B1-750 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"),
|
||||
},
|
||||
.driver_data = (void *)&acer_b1_750_info,
|
||||
},
|
||||
{
|
||||
/* Advantech MICA-071 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"),
|
||||
},
|
||||
.driver_data = (void *)&advantech_mica_071_info,
|
||||
},
|
||||
{
|
||||
/* Asus MeMO Pad 7 ME176C */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"),
|
||||
},
|
||||
.driver_data = (void *)&asus_me176c_info,
|
||||
},
|
||||
{
|
||||
/* Asus TF103C */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"),
|
||||
},
|
||||
.driver_data = (void *)&asus_tf103c_info,
|
||||
},
|
||||
{
|
||||
/* Chuwi Hi8 (CWI509) */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"),
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ilife"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "S806"),
|
||||
},
|
||||
.driver_data = (void *)&chuwi_hi8_info,
|
||||
},
|
||||
{
|
||||
/* CZC P10T */
|
||||
.ident = "CZC ODEON TPC-10 (\"P10T\")",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "CZC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"),
|
||||
},
|
||||
.driver_data = (void *)&czc_p10t,
|
||||
},
|
||||
{
|
||||
/* CZC P10T variant */
|
||||
.ident = "ViewSonic ViewPad 10",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"),
|
||||
},
|
||||
.driver_data = (void *)&czc_p10t,
|
||||
},
|
||||
{
|
||||
/* Lenovo Yoga Book X90F / X90L */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),
|
||||
},
|
||||
.driver_data = (void *)&lenovo_yogabook_x90_info,
|
||||
},
|
||||
{
|
||||
/* Lenovo Yoga Book X91F / X91L */
|
||||
.matches = {
|
||||
/* Non exact match to match F + L versions */
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"),
|
||||
},
|
||||
.driver_data = (void *)&lenovo_yogabook_x91_info,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10"
|
||||
* Lenovo Yoga Tablet 2 use the same mainboard)
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
|
||||
/* Partial match on beginning of BIOS version */
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
|
||||
},
|
||||
.driver_data = (void *)&lenovo_yoga_tab2_830_1050_info,
|
||||
},
|
||||
{
|
||||
/* Lenovo Yoga Tab 3 Pro YT3-X90F */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
|
||||
},
|
||||
.driver_data = (void *)&lenovo_yt3_info,
|
||||
},
|
||||
{
|
||||
/* Medion Lifetab S10346 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
|
||||
/* Above strings are much too generic, also match on BIOS date */
|
||||
DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"),
|
||||
},
|
||||
.driver_data = (void *)&medion_lifetab_s10346_info,
|
||||
},
|
||||
{
|
||||
/* Nextbook Ares 8 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"),
|
||||
},
|
||||
.driver_data = (void *)&nextbook_ares8_info,
|
||||
},
|
||||
{
|
||||
/* Peaq C1010 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"),
|
||||
},
|
||||
.driver_data = (void *)&peaq_c1010_info,
|
||||
},
|
||||
{
|
||||
/* Whitelabel (sold as various brands) TM800A550L */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
|
||||
/* Above strings are too generic, also match on BIOS version */
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
|
||||
},
|
||||
.driver_data = (void *)&whitelabel_tm800a550l_info,
|
||||
},
|
||||
{
|
||||
/* Xiaomi Mi Pad 2 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
|
||||
},
|
||||
.driver_data = (void *)&xiaomi_mipad2_info,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids);
|
679
drivers/platform/x86/x86-android-tablets/lenovo.c
Normal file
679
drivers/platform/x86/x86-android-tablets/lenovo.c
Normal file
@ -0,0 +1,679 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Board info for Lenovo X86 tablets which ship with Android as the factory image
|
||||
* and which have broken DSDT tables. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
#include <linux/platform_data/lp855x.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "shared-psy-info.h"
|
||||
#include "x86-android-tablets.h"
|
||||
|
||||
/*
|
||||
* Various Lenovo models use a TI LP8557 LED backlight controller with its PWM
|
||||
* input connected to a PWM output coming from the LCD panel's controller.
|
||||
* The Android kernels have a hack in the i915 driver to write a non-standard
|
||||
* panel specific DSI register to set the duty-cycle of the LCD's PWM output.
|
||||
*
|
||||
* To avoid having to have a similar hack in the mainline kernel program the
|
||||
* LP8557 to directly set the level and use the lp855x_bl driver for control.
|
||||
*/
|
||||
static struct lp855x_platform_data lenovo_lp8557_pdata = {
|
||||
.device_control = 0x86,
|
||||
.initial_brightness = 128,
|
||||
};
|
||||
|
||||
/* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */
|
||||
|
||||
static const struct property_entry lenovo_yb1_x90_wacom_props[] = {
|
||||
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001),
|
||||
PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 150),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node lenovo_yb1_x90_wacom_node = {
|
||||
.properties = lenovo_yb1_x90_wacom_props,
|
||||
};
|
||||
|
||||
/*
|
||||
* The HiDeep IST940E touchscreen comes up in I2C-HID mode. The native protocol
|
||||
* reports ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR which are not reported in HID
|
||||
* mode, so using native mode is preferred.
|
||||
* It could alternatively be used in HID mode by changing the properties to:
|
||||
* PROPERTY_ENTRY_U32("hid-descr-addr", 0x0020),
|
||||
* PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
|
||||
* and changing board_info.type to "hid-over-i2c".
|
||||
*/
|
||||
static const struct property_entry lenovo_yb1_x90_hideep_ts_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1200),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1920),
|
||||
PROPERTY_ENTRY_U32("touchscreen-max-pressure", 16384),
|
||||
PROPERTY_ENTRY_BOOL("hideep,force-native-protocol"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node lenovo_yb1_x90_hideep_ts_node = {
|
||||
.properties = lenovo_yb1_x90_hideep_ts_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* BQ27542 fuel-gauge */
|
||||
.board_info = {
|
||||
.type = "bq27542",
|
||||
.addr = 0x55,
|
||||
.dev_name = "bq27542",
|
||||
.swnode = &fg_bq25890_supply_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
}, {
|
||||
/* Goodix Touchscreen in keyboard half */
|
||||
.board_info = {
|
||||
.type = "GDIX1001:00",
|
||||
.addr = 0x14,
|
||||
.dev_name = "goodix_ts",
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C2",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FF:01",
|
||||
.index = 56,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
}, {
|
||||
/* Wacom Digitizer in keyboard half */
|
||||
.board_info = {
|
||||
.type = "hid-over-i2c",
|
||||
.addr = 0x09,
|
||||
.dev_name = "wacom",
|
||||
.swnode = &lenovo_yb1_x90_wacom_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C4",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FF:01",
|
||||
.index = 49,
|
||||
.trigger = ACPI_LEVEL_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
}, {
|
||||
/* LP8557 Backlight controller */
|
||||
.board_info = {
|
||||
.type = "lp8557",
|
||||
.addr = 0x2c,
|
||||
.dev_name = "lp8557",
|
||||
.platform_data = &lenovo_lp8557_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C4",
|
||||
}, {
|
||||
/* HiDeep IST940E Touchscreen in display half */
|
||||
.board_info = {
|
||||
.type = "hideep_ts",
|
||||
.addr = 0x6c,
|
||||
.dev_name = "hideep_ts",
|
||||
.swnode = &lenovo_yb1_x90_hideep_ts_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C6",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FF:03",
|
||||
.index = 77,
|
||||
.trigger = ACPI_LEVEL_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = {
|
||||
{
|
||||
.name = "yogabook-touch-kbd-digitizer-switch",
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table lenovo_yb1_x90_goodix_gpios = {
|
||||
.dev_id = "i2c-goodix_ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FF:01", 53, "reset", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FF:01", 56, "irq", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table lenovo_yb1_x90_hideep_gpios = {
|
||||
.dev_id = "i2c-hideep_ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table lenovo_yb1_x90_wacom_gpios = {
|
||||
.dev_id = "i2c-wacom",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FF:00", 82, "reset", GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = {
|
||||
&lenovo_yb1_x90_hideep_gpios,
|
||||
&lenovo_yb1_x90_goodix_gpios,
|
||||
&lenovo_yb1_x90_wacom_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int __init lenovo_yb1_x90_init(void)
|
||||
{
|
||||
/* Enable the regulators used by the touchscreens */
|
||||
|
||||
/* Vprog3B 3.0V used by the goodix touchscreen in the keyboard half */
|
||||
intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9b, 0x02, 0xff);
|
||||
|
||||
/* Vprog4D 3.0V used by the HiDeep touchscreen in the display half */
|
||||
intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9f, 0x02, 0xff);
|
||||
|
||||
/* Vprog5A 1.8V used by the HiDeep touchscreen in the display half */
|
||||
intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa0, 0x02, 0xff);
|
||||
|
||||
/* Vprog5B 1.8V used by the goodix touchscreen in the keyboard half */
|
||||
intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa1, 0x02, 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct x86_dev_info lenovo_yogabook_x90_info __initconst = {
|
||||
.i2c_client_info = lenovo_yb1_x90_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(lenovo_yb1_x90_i2c_clients),
|
||||
.pdev_info = lenovo_yb1_x90_pdevs,
|
||||
.pdev_count = ARRAY_SIZE(lenovo_yb1_x90_pdevs),
|
||||
.gpiod_lookup_tables = lenovo_yb1_x90_gpios,
|
||||
.init = lenovo_yb1_x90_init,
|
||||
};
|
||||
|
||||
/* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */
|
||||
static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* BQ27542 fuel-gauge */
|
||||
.board_info = {
|
||||
.type = "bq27542",
|
||||
.addr = 0x55,
|
||||
.dev_name = "bq27542",
|
||||
.swnode = &fg_bq25890_supply_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
},
|
||||
};
|
||||
|
||||
const struct x86_dev_info lenovo_yogabook_x91_info __initconst = {
|
||||
.i2c_client_info = lenovo_yogabook_x91_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x91_i2c_clients),
|
||||
};
|
||||
|
||||
/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */
|
||||
static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
|
||||
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node),
|
||||
PROPERTY_ENTRY_BOOL("omit-battery-class"),
|
||||
PROPERTY_ENTRY_BOOL("disable-reset"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = {
|
||||
.properties = lenovo_yoga_tab2_830_1050_bq24190_props,
|
||||
};
|
||||
|
||||
static struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid = {
|
||||
.button = {
|
||||
.code = SW_LID,
|
||||
.active_low = true,
|
||||
.desc = "lid_sw",
|
||||
.type = EV_SW,
|
||||
.wakeup = true,
|
||||
.debounce_interval = 50,
|
||||
},
|
||||
.chip = "INT33FC:02",
|
||||
.pin = 26,
|
||||
};
|
||||
|
||||
/* This gets filled by lenovo_yoga_tab2_830_1050_init() */
|
||||
static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { };
|
||||
|
||||
static struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initdata = {
|
||||
{
|
||||
/*
|
||||
* This must be the first entry because lenovo_yoga_tab2_830_1050_init()
|
||||
* may update its swnode. LSM303DA accelerometer + magnetometer.
|
||||
*/
|
||||
.board_info = {
|
||||
.type = "lsm303d",
|
||||
.addr = 0x1d,
|
||||
.dev_name = "lsm303d",
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
}, {
|
||||
/* bq24292i battery charger */
|
||||
.board_info = {
|
||||
.type = "bq24190",
|
||||
.addr = 0x6b,
|
||||
.dev_name = "bq24292i",
|
||||
.swnode = &lenovo_yoga_tab2_830_1050_bq24190_node,
|
||||
.platform_data = &bq24190_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C1",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 2,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
}, {
|
||||
/* BQ27541 fuel-gauge */
|
||||
.board_info = {
|
||||
.type = "bq27541",
|
||||
.addr = 0x55,
|
||||
.dev_name = "bq27541",
|
||||
.swnode = &fg_bq24190_supply_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C1",
|
||||
}, {
|
||||
/* Synaptics RMI touchscreen */
|
||||
.board_info = {
|
||||
.type = "rmi4_i2c",
|
||||
.addr = 0x38,
|
||||
.dev_name = "rmi4_i2c",
|
||||
.platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C6",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_APIC,
|
||||
.index = 0x45,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
}, {
|
||||
/* LP8557 Backlight controller */
|
||||
.board_info = {
|
||||
.type = "lp8557",
|
||||
.addr = 0x2c,
|
||||
.dev_name = "lp8557",
|
||||
.platform_data = &lenovo_lp8557_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C3",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = {
|
||||
.dev_id = "intel-int3496",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
#define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00"
|
||||
|
||||
static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = {
|
||||
.dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME,
|
||||
.table = {
|
||||
GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = {
|
||||
&lenovo_yoga_tab2_830_1050_int3496_gpios,
|
||||
&lenovo_yoga_tab2_830_1050_codec_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int __init lenovo_yoga_tab2_830_1050_init(void);
|
||||
static void lenovo_yoga_tab2_830_1050_exit(void);
|
||||
|
||||
const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = {
|
||||
.i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients),
|
||||
.pdev_info = int3496_pdevs,
|
||||
.pdev_count = 1,
|
||||
.gpio_button = &lenovo_yoga_tab2_830_1050_lid,
|
||||
.gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios,
|
||||
.bat_swnode = &generic_lipo_hv_4v35_battery_node,
|
||||
.modules = bq24190_modules,
|
||||
.init = lenovo_yoga_tab2_830_1050_init,
|
||||
.exit = lenovo_yoga_tab2_830_1050_exit,
|
||||
};
|
||||
|
||||
/*
|
||||
* The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same
|
||||
* mainboard, but the 830 uses a portrait LCD panel with a landscape touchscreen,
|
||||
* requiring the touchscreen driver to adjust the touch-coords to match the LCD.
|
||||
* And requiring the accelerometer to have a mount-matrix set to correct for
|
||||
* the 90° rotation of the LCD vs the frame.
|
||||
*/
|
||||
static const char * const lenovo_yoga_tab2_830_lms303d_mount_matrix[] = {
|
||||
"0", "1", "0",
|
||||
"-1", "0", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry lenovo_yoga_tab2_830_lms303d_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", lenovo_yoga_tab2_830_lms303d_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node lenovo_yoga_tab2_830_lms303d_node = {
|
||||
.properties = lenovo_yoga_tab2_830_lms303d_props,
|
||||
};
|
||||
|
||||
static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void)
|
||||
{
|
||||
struct gpio_desc *gpiod;
|
||||
int ret;
|
||||
|
||||
/* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */
|
||||
ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_get_value_cansleep(gpiod);
|
||||
if (ret) {
|
||||
pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n");
|
||||
} else {
|
||||
pr_info("detected Lenovo Yoga Tablet 2 830F/L\n");
|
||||
lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true;
|
||||
lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true;
|
||||
lenovo_yoga_tab2_830_1050_i2c_clients[0].board_info.swnode =
|
||||
&lenovo_yoga_tab2_830_lms303d_node;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */
|
||||
static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map =
|
||||
PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk",
|
||||
"INT33FC:02", "pmu_clk2_grp", "pmu_clk");
|
||||
|
||||
static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl;
|
||||
static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler;
|
||||
|
||||
static int __init lenovo_yoga_tab2_830_1050_init_codec(void)
|
||||
{
|
||||
struct device *codec_dev;
|
||||
struct pinctrl *pinctrl;
|
||||
int ret;
|
||||
|
||||
codec_dev = bus_find_device_by_name(&spi_bus_type, NULL,
|
||||
LENOVO_YOGA_TAB2_830_1050_CODEC_NAME);
|
||||
if (!codec_dev) {
|
||||
pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1);
|
||||
if (ret)
|
||||
goto err_put_device;
|
||||
|
||||
pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk");
|
||||
if (IS_ERR(pinctrl)) {
|
||||
ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n");
|
||||
goto err_unregister_mappings;
|
||||
}
|
||||
|
||||
/* We're done with the codec_dev now */
|
||||
put_device(codec_dev);
|
||||
|
||||
lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl;
|
||||
return 0;
|
||||
|
||||
err_unregister_mappings:
|
||||
pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map);
|
||||
err_put_device:
|
||||
put_device(codec_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off
|
||||
* gets used as pm_power_off handler. This causes "poweroff" on these tablets
|
||||
* to hang hard. Requiring pressing the powerbutton for 30 seconds *twice*
|
||||
* followed by a normal 3 second press to recover. Avoid this by doing an EFI
|
||||
* poweroff instead.
|
||||
*/
|
||||
static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data)
|
||||
{
|
||||
efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int __init lenovo_yoga_tab2_830_1050_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lenovo_yoga_tab2_830_1050_init_touchscreen();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lenovo_yoga_tab2_830_1050_init_codec();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */
|
||||
lenovo_yoga_tab2_830_1050_sys_off_handler =
|
||||
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
|
||||
lenovo_yoga_tab2_830_1050_power_off, NULL);
|
||||
if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler))
|
||||
return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lenovo_yoga_tab2_830_1050_exit(void)
|
||||
{
|
||||
unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler);
|
||||
|
||||
if (lenovo_yoga_tab2_830_1050_codec_pinctrl) {
|
||||
pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl);
|
||||
pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lenovo Yoga Tab 3 Pro YT3-X90F */
|
||||
|
||||
/*
|
||||
* There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers,
|
||||
* "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c.
|
||||
*/
|
||||
static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" };
|
||||
static const char * const bq25890_1_psy[] = { "bq25890-charger-1" };
|
||||
|
||||
static const struct property_entry fg_bq25890_1_supply_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node fg_bq25890_1_supply_node = {
|
||||
.properties = fg_bq25890_1_supply_props,
|
||||
};
|
||||
|
||||
/* bq25892 charger settings for the flat lipo battery behind the screen */
|
||||
static const struct property_entry lenovo_yt3_bq25892_0_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers),
|
||||
PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"),
|
||||
PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40),
|
||||
PROPERTY_ENTRY_BOOL("linux,skip-reset"),
|
||||
/* Values taken from Android Factory Image */
|
||||
PROPERTY_ENTRY_U32("ti,charge-current", 2048000),
|
||||
PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
|
||||
PROPERTY_ENTRY_U32("ti,termination-current", 128000),
|
||||
PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
|
||||
PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000),
|
||||
PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
|
||||
PROPERTY_ENTRY_U32("ti,boost-max-current", 500000),
|
||||
PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node lenovo_yt3_bq25892_0_node = {
|
||||
.properties = lenovo_yt3_bq25892_0_props,
|
||||
};
|
||||
|
||||
static const struct property_entry lenovo_yt3_hideep_ts_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1600),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 2560),
|
||||
PROPERTY_ENTRY_U32("touchscreen-max-pressure", 255),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node lenovo_yt3_hideep_ts_node = {
|
||||
.properties = lenovo_yt3_hideep_ts_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* bq27500 fuel-gauge for the flat lipo battery behind the screen */
|
||||
.board_info = {
|
||||
.type = "bq27500",
|
||||
.addr = 0x55,
|
||||
.dev_name = "bq27500_0",
|
||||
.swnode = &fg_bq25890_supply_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
}, {
|
||||
/* bq25892 charger for the flat lipo battery behind the screen */
|
||||
.board_info = {
|
||||
.type = "bq25892",
|
||||
.addr = 0x6b,
|
||||
.dev_name = "bq25892_0",
|
||||
.swnode = &lenovo_yt3_bq25892_0_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FF:01",
|
||||
.index = 5,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
}, {
|
||||
/* bq27500 fuel-gauge for the round li-ion cells in the hinge */
|
||||
.board_info = {
|
||||
.type = "bq27500",
|
||||
.addr = 0x55,
|
||||
.dev_name = "bq27500_1",
|
||||
.swnode = &fg_bq25890_1_supply_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C2",
|
||||
}, {
|
||||
/* HiDeep IST520E Touchscreen */
|
||||
.board_info = {
|
||||
.type = "hideep_ts",
|
||||
.addr = 0x6c,
|
||||
.dev_name = "hideep_ts",
|
||||
.swnode = &lenovo_yt3_hideep_ts_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C6",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FF:03",
|
||||
.index = 77,
|
||||
.trigger = ACPI_LEVEL_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
}, {
|
||||
/* LP8557 Backlight controller */
|
||||
.board_info = {
|
||||
.type = "lp8557",
|
||||
.addr = 0x2c,
|
||||
.dev_name = "lp8557",
|
||||
.platform_data = &lenovo_lp8557_pdata,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
}
|
||||
};
|
||||
|
||||
static int __init lenovo_yt3_init(void)
|
||||
{
|
||||
struct gpio_desc *gpiod;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins
|
||||
* connected to GPIOs, rather then having them hardwired to the correct
|
||||
* values as is normally done.
|
||||
*
|
||||
* The bq25890_charger driver controls these through I2C, but this only
|
||||
* works if not overridden by the pins. Set these pins here:
|
||||
* 1. Set /CE to 0 to allow charging.
|
||||
* 2. Set OTG to 0 disable V5 boost output since the 5V boost output of
|
||||
* the main "bq25892_1" charger is used when necessary.
|
||||
*/
|
||||
|
||||
/* /CE pin */
|
||||
ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw"
|
||||
* gpio_desc, that is there is no way to pass lookup-flags like
|
||||
* GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since
|
||||
* the /CE pin is active-low, but not marked as such in the gpio_desc.
|
||||
*/
|
||||
gpiod_set_value(gpiod, 0);
|
||||
|
||||
/* OTG pin */
|
||||
ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gpiod_set_value(gpiod, 0);
|
||||
|
||||
/* Enable the regulators used by the touchscreen */
|
||||
intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9b, 0x02, 0xff);
|
||||
intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa0, 0x02, 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gpiod_lookup_table lenovo_yt3_hideep_gpios = {
|
||||
.dev_id = "i2c-hideep_ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const lenovo_yt3_gpios[] = {
|
||||
&lenovo_yt3_hideep_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info lenovo_yt3_info __initconst = {
|
||||
.i2c_client_info = lenovo_yt3_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients),
|
||||
.gpiod_lookup_tables = lenovo_yt3_gpios,
|
||||
.init = lenovo_yt3_init,
|
||||
};
|
522
drivers/platform/x86/x86-android-tablets/other.c
Normal file
522
drivers/platform/x86/x86-android-tablets/other.c
Normal file
@ -0,0 +1,522 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* DMI based code to deal with broken DSDTs on X86 tablets which ship with
|
||||
* Android as (part of) the factory image. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "shared-psy-info.h"
|
||||
#include "x86-android-tablets.h"
|
||||
|
||||
/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */
|
||||
static const char * const acer_b1_750_mount_matrix[] = {
|
||||
"-1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry acer_b1_750_bma250e_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node acer_b1_750_bma250e_node = {
|
||||
.properties = acer_b1_750_bma250e_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* Novatek NVT-ts touchscreen */
|
||||
.board_info = {
|
||||
.type = "NVT-ts",
|
||||
.addr = 0x34,
|
||||
.dev_name = "NVT-ts",
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C4",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 3,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
}, {
|
||||
/* BMA250E accelerometer */
|
||||
.board_info = {
|
||||
.type = "bma250e",
|
||||
.addr = 0x18,
|
||||
.swnode = &acer_b1_750_bma250e_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C3",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 25,
|
||||
.trigger = ACPI_LEVEL_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table acer_b1_750_goodix_gpios = {
|
||||
.dev_id = "i2c-NVT-ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const acer_b1_750_gpios[] = {
|
||||
&acer_b1_750_goodix_gpios,
|
||||
&int3496_reference_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info acer_b1_750_info __initconst = {
|
||||
.i2c_client_info = acer_b1_750_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients),
|
||||
.pdev_info = int3496_pdevs,
|
||||
.pdev_count = 1,
|
||||
.gpiod_lookup_tables = acer_b1_750_gpios,
|
||||
};
|
||||
|
||||
/*
|
||||
* Advantech MICA-071
|
||||
* This is a standard Windows tablet, but it has an extra "quick launch" button
|
||||
* which is not described in the ACPI tables in anyway.
|
||||
* Use the x86-android-tablets infra to create a gpio-button device for this.
|
||||
*/
|
||||
static struct x86_gpio_button advantech_mica_071_button = {
|
||||
.button = {
|
||||
.code = KEY_PROG1,
|
||||
.active_low = true,
|
||||
.desc = "prog1_key",
|
||||
.type = EV_KEY,
|
||||
.wakeup = false,
|
||||
.debounce_interval = 50,
|
||||
},
|
||||
.chip = "INT33FC:00",
|
||||
.pin = 2,
|
||||
};
|
||||
|
||||
const struct x86_dev_info advantech_mica_071_info __initconst = {
|
||||
.gpio_button = &advantech_mica_071_button,
|
||||
};
|
||||
|
||||
/*
|
||||
* When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT
|
||||
* contains a whole bunch of bogus ACPI I2C devices and is missing entries
|
||||
* for the touchscreen and the accelerometer.
|
||||
*/
|
||||
static const struct property_entry chuwi_hi8_gsl1680_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1665),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node chuwi_hi8_gsl1680_node = {
|
||||
.properties = chuwi_hi8_gsl1680_props,
|
||||
};
|
||||
|
||||
static const char * const chuwi_hi8_mount_matrix[] = {
|
||||
"1", "0", "0",
|
||||
"0", "-1", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry chuwi_hi8_bma250e_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node chuwi_hi8_bma250e_node = {
|
||||
.properties = chuwi_hi8_bma250e_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* Silead touchscreen */
|
||||
.board_info = {
|
||||
.type = "gsl1680",
|
||||
.addr = 0x40,
|
||||
.swnode = &chuwi_hi8_gsl1680_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C4",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_APIC,
|
||||
.index = 0x44,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
}, {
|
||||
/* BMA250E accelerometer */
|
||||
.board_info = {
|
||||
.type = "bma250e",
|
||||
.addr = 0x18,
|
||||
.swnode = &chuwi_hi8_bma250e_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C3",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 23,
|
||||
.trigger = ACPI_LEVEL_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int __init chuwi_hi8_init(void)
|
||||
{
|
||||
/*
|
||||
* Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get()
|
||||
* breaking the touchscreen + logging various errors when the Windows
|
||||
* BIOS is used.
|
||||
*/
|
||||
if (acpi_dev_present("MSSL0001", NULL, 1))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct x86_dev_info chuwi_hi8_info __initconst = {
|
||||
.i2c_client_info = chuwi_hi8_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients),
|
||||
.init = chuwi_hi8_init,
|
||||
};
|
||||
|
||||
#define CZC_EC_EXTRA_PORT 0x68
|
||||
#define CZC_EC_ANDROID_KEYS 0x63
|
||||
|
||||
static int __init czc_p10t_init(void)
|
||||
{
|
||||
/*
|
||||
* The device boots up in "Windows 7" mode, when the home button sends a
|
||||
* Windows specific key sequence (Left Meta + D) and the second button
|
||||
* sends an unknown one while also toggling the Radio Kill Switch.
|
||||
* This is a surprising behavior when the second button is labeled "Back".
|
||||
*
|
||||
* The vendor-supplied Android-x86 build switches the device to a "Android"
|
||||
* mode by writing value 0x63 to the I/O port 0x68. This just seems to just
|
||||
* set bit 6 on address 0x96 in the EC region; switching the bit directly
|
||||
* seems to achieve the same result. It uses a "p10t_switcher" to do the
|
||||
* job. It doesn't seem to be able to do anything else, and no other use
|
||||
* of the port 0x68 is known.
|
||||
*
|
||||
* In the Android mode, the home button sends just a single scancode,
|
||||
* which can be handled in Linux userspace more reasonably and the back
|
||||
* button only sends a scancode without toggling the kill switch.
|
||||
* The scancode can then be mapped either to Back or RF Kill functionality
|
||||
* in userspace, depending on how the button is labeled on that particular
|
||||
* model.
|
||||
*/
|
||||
outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct x86_dev_info czc_p10t __initconst = {
|
||||
.init = czc_p10t_init,
|
||||
};
|
||||
|
||||
/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */
|
||||
static const char * const medion_lifetab_s10346_accel_mount_matrix[] = {
|
||||
"0", "1", "0",
|
||||
"1", "0", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry medion_lifetab_s10346_accel_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node medion_lifetab_s10346_accel_node = {
|
||||
.properties = medion_lifetab_s10346_accel_props,
|
||||
};
|
||||
|
||||
/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */
|
||||
static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = {
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node medion_lifetab_s10346_touchscreen_node = {
|
||||
.properties = medion_lifetab_s10346_touchscreen_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* kxtj21009 accel */
|
||||
.board_info = {
|
||||
.type = "kxtj21009",
|
||||
.addr = 0x0f,
|
||||
.dev_name = "kxtj21009",
|
||||
.swnode = &medion_lifetab_s10346_accel_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C3",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 23,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
}, {
|
||||
/* goodix touchscreen */
|
||||
.board_info = {
|
||||
.type = "GDIX1001:00",
|
||||
.addr = 0x14,
|
||||
.dev_name = "goodix_ts",
|
||||
.swnode = &medion_lifetab_s10346_touchscreen_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C4",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_APIC,
|
||||
.index = 0x44,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = {
|
||||
.dev_id = "i2c-goodix_ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = {
|
||||
&medion_lifetab_s10346_goodix_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info medion_lifetab_s10346_info __initconst = {
|
||||
.i2c_client_info = medion_lifetab_s10346_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients),
|
||||
.gpiod_lookup_tables = medion_lifetab_s10346_gpios,
|
||||
};
|
||||
|
||||
/* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */
|
||||
static const char * const nextbook_ares8_accel_mount_matrix[] = {
|
||||
"0", "-1", "0",
|
||||
"-1", "0", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry nextbook_ares8_accel_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node nextbook_ares8_accel_node = {
|
||||
.properties = nextbook_ares8_accel_props,
|
||||
};
|
||||
|
||||
static const struct property_entry nextbook_ares8_touchscreen_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 800),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node nextbook_ares8_touchscreen_node = {
|
||||
.properties = nextbook_ares8_touchscreen_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* Freescale MMA8653FC accel */
|
||||
.board_info = {
|
||||
.type = "mma8653",
|
||||
.addr = 0x1d,
|
||||
.dev_name = "mma8653",
|
||||
.swnode = &nextbook_ares8_accel_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C3",
|
||||
}, {
|
||||
/* FT5416DQ9 touchscreen controller */
|
||||
.board_info = {
|
||||
.type = "edt-ft5x06",
|
||||
.addr = 0x38,
|
||||
.dev_name = "ft5416",
|
||||
.swnode = &nextbook_ares8_touchscreen_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C4",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
.chip = "INT33FC:02",
|
||||
.index = 3,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_LOW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = {
|
||||
&int3496_reference_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info nextbook_ares8_info __initconst = {
|
||||
.i2c_client_info = nextbook_ares8_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients),
|
||||
.pdev_info = int3496_pdevs,
|
||||
.pdev_count = 1,
|
||||
.gpiod_lookup_tables = nextbook_ares8_gpios,
|
||||
.invalid_aei_gpiochip = "INT33FC:02",
|
||||
};
|
||||
|
||||
/*
|
||||
* Peaq C1010
|
||||
* This is a standard Windows tablet, but it has a special Dolby button.
|
||||
* This button has a WMI interface, but that is broken. Instead of trying to
|
||||
* use the broken WMI interface, instantiate a gpio_keys device for this.
|
||||
*/
|
||||
static struct x86_gpio_button peaq_c1010_button = {
|
||||
.button = {
|
||||
.code = KEY_SOUND,
|
||||
.active_low = true,
|
||||
.desc = "dolby_key",
|
||||
.type = EV_KEY,
|
||||
.wakeup = false,
|
||||
.debounce_interval = 50,
|
||||
},
|
||||
.chip = "INT33FC:00",
|
||||
.pin = 3,
|
||||
};
|
||||
|
||||
const struct x86_dev_info peaq_c1010_info __initconst = {
|
||||
.gpio_button = &peaq_c1010_button,
|
||||
/*
|
||||
* Move the ACPI event handler used by the broken WMI interface out of
|
||||
* the way. This is the only event handler on INT33FC:00.
|
||||
*/
|
||||
.invalid_aei_gpiochip = "INT33FC:00",
|
||||
};
|
||||
|
||||
/*
|
||||
* Whitelabel (sold as various brands) TM800A550L tablets.
|
||||
* These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices
|
||||
* (removed through acpi_quirk_skip_i2c_client_enumeration()) and
|
||||
* the touchscreen fwnode has the wrong GPIOs.
|
||||
*/
|
||||
static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = {
|
||||
"-1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1"
|
||||
};
|
||||
|
||||
static const struct property_entry whitelabel_tm800a550l_accel_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node whitelabel_tm800a550l_accel_node = {
|
||||
.properties = whitelabel_tm800a550l_accel_props,
|
||||
};
|
||||
|
||||
static const struct property_entry whitelabel_tm800a550l_goodix_props[] = {
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"),
|
||||
PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"),
|
||||
PROPERTY_ENTRY_U32("goodix,main-clk", 54),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node whitelabel_tm800a550l_goodix_node = {
|
||||
.properties = whitelabel_tm800a550l_goodix_props,
|
||||
};
|
||||
|
||||
static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* goodix touchscreen */
|
||||
.board_info = {
|
||||
.type = "GDIX1001:00",
|
||||
.addr = 0x14,
|
||||
.dev_name = "goodix_ts",
|
||||
.swnode = &whitelabel_tm800a550l_goodix_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C2",
|
||||
.irq_data = {
|
||||
.type = X86_ACPI_IRQ_TYPE_APIC,
|
||||
.index = 0x44,
|
||||
.trigger = ACPI_EDGE_SENSITIVE,
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
}, {
|
||||
/* kxcj91008 accel */
|
||||
.board_info = {
|
||||
.type = "kxcj91008",
|
||||
.addr = 0x0f,
|
||||
.dev_name = "kxcj91008",
|
||||
.swnode = &whitelabel_tm800a550l_accel_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C3",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = {
|
||||
.dev_id = "i2c-goodix_ts",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = {
|
||||
&whitelabel_tm800a550l_goodix_gpios,
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
|
||||
.i2c_client_info = whitelabel_tm800a550l_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients),
|
||||
.gpiod_lookup_tables = whitelabel_tm800a550l_gpios,
|
||||
};
|
||||
|
||||
/*
|
||||
* If the EFI bootloader is not Xiaomi's own signed Android loader, then the
|
||||
* Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing
|
||||
* a bunch of devices to be hidden.
|
||||
*
|
||||
* This takes care of instantiating the hidden devices manually.
|
||||
*/
|
||||
static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* BQ27520 fuel-gauge */
|
||||
.board_info = {
|
||||
.type = "bq27520",
|
||||
.addr = 0x55,
|
||||
.dev_name = "bq27520",
|
||||
.swnode = &fg_bq25890_supply_node,
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
}, {
|
||||
/* KTD2026 RGB notification LED controller */
|
||||
.board_info = {
|
||||
.type = "ktd2026",
|
||||
.addr = 0x30,
|
||||
.dev_name = "ktd2026",
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C3",
|
||||
},
|
||||
};
|
||||
|
||||
const struct x86_dev_info xiaomi_mipad2_info __initconst = {
|
||||
.i2c_client_info = xiaomi_mipad2_i2c_clients,
|
||||
.i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients),
|
||||
};
|
100
drivers/platform/x86/x86-android-tablets/shared-psy-info.c
Normal file
100
drivers/platform/x86/x86-android-tablets/shared-psy-info.c
Normal file
@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Shared psy info for X86 tablets which ship with Android as the factory image
|
||||
* and which have broken DSDT tables. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power/bq24190_charger.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
#include "shared-psy-info.h"
|
||||
|
||||
/* Generic / shared charger / battery settings */
|
||||
const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" };
|
||||
const char * const bq24190_psy[] = { "bq24190-charger" };
|
||||
const char * const bq25890_psy[] = { "bq25890-charger-0" };
|
||||
|
||||
static const struct property_entry fg_bq24190_supply_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy),
|
||||
{ }
|
||||
};
|
||||
|
||||
const struct software_node fg_bq24190_supply_node = {
|
||||
.properties = fg_bq24190_supply_props,
|
||||
};
|
||||
|
||||
static const struct property_entry fg_bq25890_supply_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy),
|
||||
{ }
|
||||
};
|
||||
|
||||
const struct software_node fg_bq25890_supply_node = {
|
||||
.properties = fg_bq25890_supply_props,
|
||||
};
|
||||
|
||||
/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */
|
||||
static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
|
||||
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
||||
PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"),
|
||||
PROPERTY_ENTRY_U32("precharge-current-microamp", 256000),
|
||||
PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000),
|
||||
PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000),
|
||||
PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000),
|
||||
PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000),
|
||||
{ }
|
||||
};
|
||||
|
||||
const struct software_node generic_lipo_hv_4v35_battery_node = {
|
||||
.properties = generic_lipo_hv_4v35_battery_props,
|
||||
};
|
||||
|
||||
/* For enabling the bq24190 5V boost based on id-pin */
|
||||
static struct regulator_consumer_supply intel_int3496_consumer = {
|
||||
.supply = "vbus",
|
||||
.dev_name = "intel-int3496",
|
||||
};
|
||||
|
||||
static const struct regulator_init_data bq24190_vbus_init_data = {
|
||||
.constraints = {
|
||||
.name = "bq24190_vbus",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.consumer_supplies = &intel_int3496_consumer,
|
||||
.num_consumer_supplies = 1,
|
||||
};
|
||||
|
||||
struct bq24190_platform_data bq24190_pdata = {
|
||||
.regulator_init_data = &bq24190_vbus_init_data,
|
||||
};
|
||||
|
||||
const char * const bq24190_modules[] __initconst = {
|
||||
"intel_crystal_cove_charger", /* For the bq24190 IRQ */
|
||||
"bq24190_charger", /* For the Vbus regulator for intel-int3496 */
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */
|
||||
const struct platform_device_info int3496_pdevs[] __initconst = {
|
||||
{
|
||||
/* For micro USB ID pin handling */
|
||||
.name = "intel-int3496",
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
},
|
||||
};
|
||||
|
||||
struct gpiod_lookup_table int3496_reference_gpios = {
|
||||
.dev_id = "intel-int3496",
|
||||
.table = {
|
||||
GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
32
drivers/platform/x86/x86-android-tablets/shared-psy-info.h
Normal file
32
drivers/platform/x86/x86-android-tablets/shared-psy-info.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Shared psy info for X86 tablets which ship with Android as the factory image
|
||||
* and which have broken DSDT tables. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
#ifndef __PDX86_SHARED_PSY_INFO_H
|
||||
#define __PDX86_SHARED_PSY_INFO_H
|
||||
|
||||
struct bq24190_platform_data;
|
||||
struct gpiod_lookup_table;
|
||||
struct platform_device_info;
|
||||
struct software_node;
|
||||
|
||||
extern const char * const tusb1211_chg_det_psy[];
|
||||
extern const char * const bq24190_psy[];
|
||||
extern const char * const bq25890_psy[];
|
||||
|
||||
extern const struct software_node fg_bq24190_supply_node;
|
||||
extern const struct software_node fg_bq25890_supply_node;
|
||||
extern const struct software_node generic_lipo_hv_4v35_battery_node;
|
||||
|
||||
extern struct bq24190_platform_data bq24190_pdata;
|
||||
extern const char * const bq24190_modules[];
|
||||
|
||||
extern const struct platform_device_info int3496_pdevs[];
|
||||
extern struct gpiod_lookup_table int3496_reference_gpios;
|
||||
|
||||
#endif
|
108
drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
Normal file
108
drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
Normal file
@ -0,0 +1,108 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* DMI based code to deal with broken DSDTs on X86 tablets which ship with
|
||||
* Android as (part of) the factory image. The factory kernels shipped on these
|
||||
* devices typically have a bunch of things hardcoded, rather than specified
|
||||
* in their DSDT.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
#ifndef __PDX86_X86_ANDROID_TABLETS_H
|
||||
#define __PDX86_X86_ANDROID_TABLETS_H
|
||||
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/irqdomain_defs.h>
|
||||
|
||||
struct gpio_desc;
|
||||
struct gpiod_lookup_table;
|
||||
struct platform_device_info;
|
||||
struct software_node;
|
||||
|
||||
/*
|
||||
* Helpers to get Linux IRQ numbers given a description of the IRQ source
|
||||
* (either IOAPIC index, or GPIO chip name + pin-number).
|
||||
*/
|
||||
enum x86_acpi_irq_type {
|
||||
X86_ACPI_IRQ_TYPE_NONE,
|
||||
X86_ACPI_IRQ_TYPE_APIC,
|
||||
X86_ACPI_IRQ_TYPE_GPIOINT,
|
||||
X86_ACPI_IRQ_TYPE_PMIC,
|
||||
};
|
||||
|
||||
struct x86_acpi_irq_data {
|
||||
char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */
|
||||
enum x86_acpi_irq_type type;
|
||||
enum irq_domain_bus_token domain;
|
||||
int index;
|
||||
int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */
|
||||
int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */
|
||||
};
|
||||
|
||||
/* Structs to describe devices to instantiate */
|
||||
struct x86_i2c_client_info {
|
||||
struct i2c_board_info board_info;
|
||||
char *adapter_path;
|
||||
struct x86_acpi_irq_data irq_data;
|
||||
};
|
||||
|
||||
struct x86_serdev_info {
|
||||
const char *ctrl_hid;
|
||||
const char *ctrl_uid;
|
||||
const char *ctrl_devname;
|
||||
/*
|
||||
* ATM the serdev core only supports of or ACPI matching; and sofar all
|
||||
* Android x86 tablets DSDTs have usable serdev nodes, but sometimes
|
||||
* under the wrong controller. So we just tie the existing serdev ACPI
|
||||
* node to the right controller.
|
||||
*/
|
||||
const char *serdev_hid;
|
||||
};
|
||||
|
||||
struct x86_gpio_button {
|
||||
struct gpio_keys_button button;
|
||||
const char *chip;
|
||||
int pin;
|
||||
};
|
||||
|
||||
struct x86_dev_info {
|
||||
char *invalid_aei_gpiochip;
|
||||
const char * const *modules;
|
||||
const struct software_node *bat_swnode;
|
||||
struct gpiod_lookup_table * const *gpiod_lookup_tables;
|
||||
const struct x86_i2c_client_info *i2c_client_info;
|
||||
const struct platform_device_info *pdev_info;
|
||||
const struct x86_serdev_info *serdev_info;
|
||||
struct x86_gpio_button *gpio_button;
|
||||
int i2c_client_count;
|
||||
int pdev_count;
|
||||
int serdev_count;
|
||||
int (*init)(void);
|
||||
void (*exit)(void);
|
||||
};
|
||||
|
||||
int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc);
|
||||
int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data);
|
||||
|
||||
/*
|
||||
* Extern declarations of x86_dev_info structs so there can be a single
|
||||
* MODULE_DEVICE_TABLE(dmi, ...), while splitting the board descriptions.
|
||||
*/
|
||||
extern const struct x86_dev_info acer_b1_750_info;
|
||||
extern const struct x86_dev_info advantech_mica_071_info;
|
||||
extern const struct x86_dev_info asus_me176c_info;
|
||||
extern const struct x86_dev_info asus_tf103c_info;
|
||||
extern const struct x86_dev_info chuwi_hi8_info;
|
||||
extern const struct x86_dev_info czc_p10t;
|
||||
extern const struct x86_dev_info lenovo_yogabook_x90_info;
|
||||
extern const struct x86_dev_info lenovo_yogabook_x91_info;
|
||||
extern const struct x86_dev_info lenovo_yoga_tab2_830_1050_info;
|
||||
extern const struct x86_dev_info lenovo_yt3_info;
|
||||
extern const struct x86_dev_info medion_lifetab_s10346_info;
|
||||
extern const struct x86_dev_info nextbook_ares8_info;
|
||||
extern const struct x86_dev_info peaq_c1010_info;
|
||||
extern const struct x86_dev_info whitelabel_tm800a550l_info;
|
||||
extern const struct x86_dev_info xiaomi_mipad2_info;
|
||||
extern const struct dmi_system_id x86_android_tablet_ids[];
|
||||
|
||||
#endif
|
@ -56,12 +56,11 @@ static int xo1_rfkill_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xo1_rfkill_remove(struct platform_device *pdev)
|
||||
static void xo1_rfkill_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rfkill *rfk = platform_get_drvdata(pdev);
|
||||
rfkill_unregister(rfk);
|
||||
rfkill_destroy(rfk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver xo1_rfkill_driver = {
|
||||
@ -69,7 +68,7 @@ static struct platform_driver xo1_rfkill_driver = {
|
||||
.name = "xo1-rfkill",
|
||||
},
|
||||
.probe = xo1_rfkill_probe,
|
||||
.remove = xo1_rfkill_remove,
|
||||
.remove_new = xo1_rfkill_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(xo1_rfkill_driver);
|
||||
|
@ -285,6 +285,7 @@ config BACKLIGHT_MT6370
|
||||
config BACKLIGHT_APPLE
|
||||
tristate "Apple Backlight Driver"
|
||||
depends on X86 && ACPI
|
||||
depends on ACPI_VIDEO=n || ACPI_VIDEO
|
||||
help
|
||||
If you have an Intel-based Apple say Y to enable a driver for its
|
||||
backlight.
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/apple_bl.h>
|
||||
#include <acpi/video.h>
|
||||
|
||||
static struct backlight_device *apple_backlight_device;
|
||||
|
||||
@ -215,32 +215,21 @@ static struct acpi_driver apple_bl_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static atomic_t apple_bl_registered = ATOMIC_INIT(0);
|
||||
|
||||
int apple_bl_register(void)
|
||||
{
|
||||
if (atomic_xchg(&apple_bl_registered, 1) == 0)
|
||||
return acpi_bus_register_driver(&apple_bl_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(apple_bl_register);
|
||||
|
||||
void apple_bl_unregister(void)
|
||||
{
|
||||
if (atomic_xchg(&apple_bl_registered, 0) == 1)
|
||||
acpi_bus_unregister_driver(&apple_bl_driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(apple_bl_unregister);
|
||||
|
||||
static int __init apple_bl_init(void)
|
||||
{
|
||||
return apple_bl_register();
|
||||
/*
|
||||
* Use ACPI video detection code to see if this driver should register
|
||||
* or if another driver, e.g. the apple-gmux driver should be used.
|
||||
*/
|
||||
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
|
||||
return -ENODEV;
|
||||
|
||||
return acpi_bus_register_driver(&apple_bl_driver);
|
||||
}
|
||||
|
||||
static void __exit apple_bl_exit(void)
|
||||
{
|
||||
apple_bl_unregister();
|
||||
acpi_bus_unregister_driver(&apple_bl_driver);
|
||||
}
|
||||
|
||||
module_init(apple_bl_init);
|
||||
|
@ -34,8 +34,20 @@
|
||||
#define GMUX_PORT_READ 0xd0
|
||||
#define GMUX_PORT_WRITE 0xd4
|
||||
|
||||
#define GMUX_MMIO_PORT_SELECT 0x0e
|
||||
#define GMUX_MMIO_COMMAND_SEND 0x0f
|
||||
|
||||
#define GMUX_MMIO_READ 0x00
|
||||
#define GMUX_MMIO_WRITE 0x40
|
||||
|
||||
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
|
||||
|
||||
enum apple_gmux_type {
|
||||
APPLE_GMUX_TYPE_PIO,
|
||||
APPLE_GMUX_TYPE_INDEXED,
|
||||
APPLE_GMUX_TYPE_MMIO,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_APPLE_GMUX)
|
||||
static inline bool apple_gmux_is_indexed(unsigned long iostart)
|
||||
{
|
||||
@ -52,11 +64,29 @@ static inline bool apple_gmux_is_indexed(unsigned long iostart)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool apple_gmux_is_mmio(unsigned long iostart)
|
||||
{
|
||||
u8 __iomem *iomem_base = ioremap(iostart, 16);
|
||||
u8 val;
|
||||
|
||||
if (!iomem_base)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If this is 0xff, then gmux must not be present, as the gmux would
|
||||
* reset it to 0x00, or it would be one of 0x1, 0x4, 0x41, 0x44 if a
|
||||
* command is currently being processed.
|
||||
*/
|
||||
val = ioread8(iomem_base + GMUX_MMIO_COMMAND_SEND);
|
||||
iounmap(iomem_base);
|
||||
return (val != 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* apple_gmux_detect() - detect if gmux is built into the machine
|
||||
*
|
||||
* @pnp_dev: Device to probe or NULL to use the first matching device
|
||||
* @indexed_ret: Returns (by reference) if the gmux is indexed or not
|
||||
* @type_ret: Returns (by reference) the apple_gmux_type of the device
|
||||
*
|
||||
* Detect if a supported gmux device is present by actually probing it.
|
||||
* This avoids the false positives returned on some models by
|
||||
@ -65,13 +95,13 @@ static inline bool apple_gmux_is_indexed(unsigned long iostart)
|
||||
* Return: %true if a supported gmux ACPI device is detected and the kernel
|
||||
* was configured with CONFIG_APPLE_GMUX, %false otherwise.
|
||||
*/
|
||||
static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
|
||||
static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_type *type_ret)
|
||||
{
|
||||
u8 ver_major, ver_minor, ver_release;
|
||||
struct device *dev = NULL;
|
||||
struct acpi_device *adev;
|
||||
struct resource *res;
|
||||
bool indexed = false;
|
||||
enum apple_gmux_type type = APPLE_GMUX_TYPE_PIO;
|
||||
bool ret = false;
|
||||
|
||||
if (!pnp_dev) {
|
||||
@ -88,24 +118,30 @@ static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
|
||||
}
|
||||
|
||||
res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0);
|
||||
if (!res || resource_size(res) < GMUX_MIN_IO_LEN)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Invalid version information may indicate either that the gmux
|
||||
* device isn't present or that it's a new one that uses indexed io.
|
||||
*/
|
||||
ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR);
|
||||
ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR);
|
||||
ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE);
|
||||
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
|
||||
indexed = apple_gmux_is_indexed(res->start);
|
||||
if (!indexed)
|
||||
if (res && resource_size(res) >= GMUX_MIN_IO_LEN) {
|
||||
/*
|
||||
* Invalid version information may indicate either that the gmux
|
||||
* device isn't present or that it's a new one that uses indexed io.
|
||||
*/
|
||||
ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR);
|
||||
ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR);
|
||||
ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE);
|
||||
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
|
||||
if (apple_gmux_is_indexed(res->start))
|
||||
type = APPLE_GMUX_TYPE_INDEXED;
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0);
|
||||
if (res && apple_gmux_is_mmio(res->start))
|
||||
type = APPLE_GMUX_TYPE_MMIO;
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (indexed_ret)
|
||||
*indexed_ret = indexed;
|
||||
if (type_ret)
|
||||
*type_ret = type;
|
||||
|
||||
ret = true;
|
||||
out:
|
||||
|
@ -1,27 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* apple_bl exported symbols
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_APPLE_BL_H
|
||||
#define _LINUX_APPLE_BL_H
|
||||
|
||||
#if defined(CONFIG_BACKLIGHT_APPLE) || defined(CONFIG_BACKLIGHT_APPLE_MODULE)
|
||||
|
||||
extern int apple_bl_register(void);
|
||||
extern void apple_bl_unregister(void);
|
||||
|
||||
#else /* !CONFIG_BACKLIGHT_APPLE */
|
||||
|
||||
static inline int apple_bl_register(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void apple_bl_unregister(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_BACKLIGHT_APPLE */
|
||||
|
||||
#endif /* _LINUX_APPLE_BL_H */
|
@ -163,10 +163,313 @@ struct isst_if_msr_cmds {
|
||||
struct isst_if_msr_cmd msr_cmd[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_core_power - Structure to get/set core_power feature
|
||||
* @get_set: 0: Get, 1: Set
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @enable: Feature enable status
|
||||
* @priority_type: Priority type for the feature (ordered/proportional)
|
||||
*
|
||||
* Structure to get/set core_power feature state using IOCTL
|
||||
* ISST_IF_CORE_POWER_STATE.
|
||||
*/
|
||||
struct isst_core_power {
|
||||
__u8 get_set;
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 enable;
|
||||
__u8 supported;
|
||||
__u8 priority_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_clos_param - Structure to get/set clos praram
|
||||
* @get_set: 0: Get, 1: Set
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* clos: Clos ID for the parameters
|
||||
* min_freq_mhz: Minimum frequency in MHz
|
||||
* max_freq_mhz: Maximum frequency in MHz
|
||||
* prop_prio: Proportional priority from 0-15
|
||||
*
|
||||
* Structure to get/set per clos property using IOCTL
|
||||
* ISST_IF_CLOS_PARAM.
|
||||
*/
|
||||
struct isst_clos_param {
|
||||
__u8 get_set;
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 clos;
|
||||
__u16 min_freq_mhz;
|
||||
__u16 max_freq_mhz;
|
||||
__u8 prop_prio;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_if_clos_assoc - Structure to assign clos to a CPU
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @logical_cpu: CPU number
|
||||
* @clos: Clos ID to assign to the logical CPU
|
||||
*
|
||||
* Structure to get/set core_power feature.
|
||||
*/
|
||||
struct isst_if_clos_assoc {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u16 logical_cpu;
|
||||
__u16 clos;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_if_clos_assoc_cmds - Structure to assign clos to CPUs
|
||||
* @cmd_count: Number of cmds (cpus) in this request
|
||||
* @get_set: Request is for get or set
|
||||
* @punit_cpu_map: Set to 1 if the CPU number is punit numbering not
|
||||
* Linux CPU number
|
||||
*
|
||||
* Structure used to get/set associate CPUs to clos using IOCTL
|
||||
* ISST_IF_CLOS_ASSOC.
|
||||
*/
|
||||
struct isst_if_clos_assoc_cmds {
|
||||
__u16 cmd_count;
|
||||
__u16 get_set;
|
||||
__u16 punit_cpu_map;
|
||||
struct isst_if_clos_assoc assoc_info[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_tpmi_instance_count - Get number of TPMI instances per socket
|
||||
* @socket_id: Socket/package id
|
||||
* @count: Number of instances
|
||||
* @valid_mask: Mask of instances as there can be holes
|
||||
*
|
||||
* Structure used to get TPMI instances information using
|
||||
* IOCTL ISST_IF_COUNT_TPMI_INSTANCES.
|
||||
*/
|
||||
struct isst_tpmi_instance_count {
|
||||
__u8 socket_id;
|
||||
__u8 count;
|
||||
__u16 valid_mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_perf_level_info - Structure to get information on SST-PP levels
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @logical_cpu: CPU number
|
||||
* @clos: Clos ID to assign to the logical CPU
|
||||
* @max_level: Maximum performance level supported by the platform
|
||||
* @feature_rev: The feature revision for SST-PP supported by the platform
|
||||
* @level_mask: Mask of supported performance levels
|
||||
* @current_level: Current performance level
|
||||
* @feature_state: SST-BF and SST-TF (enabled/disabled) status at current level
|
||||
* @locked: SST-PP performance level change is locked/unlocked
|
||||
* @enabled: SST-PP feature is enabled or not
|
||||
* @sst-tf_support: SST-TF support status at this level
|
||||
* @sst-bf_support: SST-BF support status at this level
|
||||
*
|
||||
* Structure to get SST-PP details using IOCTL ISST_IF_PERF_LEVELS.
|
||||
*/
|
||||
struct isst_perf_level_info {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 max_level;
|
||||
__u8 feature_rev;
|
||||
__u8 level_mask;
|
||||
__u8 current_level;
|
||||
__u8 feature_state;
|
||||
__u8 locked;
|
||||
__u8 enabled;
|
||||
__u8 sst_tf_support;
|
||||
__u8 sst_bf_support;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_perf_level_control - Structure to set SST-PP level
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @level: level to set
|
||||
*
|
||||
* Structure used change SST-PP level using IOCTL ISST_IF_PERF_SET_LEVEL.
|
||||
*/
|
||||
struct isst_perf_level_control {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 level;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_perf_feature_control - Structure to activate SST-BF/SST-TF
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @feature: bit 0 = SST-BF state, bit 1 = SST-TF state
|
||||
*
|
||||
* Structure used to enable SST-BF/SST-TF using IOCTL ISST_IF_PERF_SET_FEATURE.
|
||||
*/
|
||||
struct isst_perf_feature_control {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 feature;
|
||||
};
|
||||
|
||||
#define TRL_MAX_BUCKETS 8
|
||||
#define TRL_MAX_LEVELS 6
|
||||
|
||||
/**
|
||||
* struct isst_perf_level_data_info - Structure to get SST-PP level details
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @level: SST-PP level for which caller wants to get information
|
||||
* @tdp_ratio: TDP Ratio
|
||||
* @base_freq_mhz: Base frequency in MHz
|
||||
* @base_freq_avx2_mhz: AVX2 Base frequency in MHz
|
||||
* @base_freq_avx512_mhz: AVX512 base frequency in MHz
|
||||
* @base_freq_amx_mhz: AMX base frequency in MHz
|
||||
* @thermal_design_power_w: Thermal design (TDP) power
|
||||
* @tjunction_max_c: Max junction temperature
|
||||
* @max_memory_freq_mhz: Max memory frequency in MHz
|
||||
* @cooling_type: Type of cooling is used
|
||||
* @p0_freq_mhz: core maximum frequency
|
||||
* @p1_freq_mhz: Core TDP frequency
|
||||
* @pn_freq_mhz: Core maximum efficiency frequency
|
||||
* @pm_freq_mhz: Core minimum frequency
|
||||
* @p0_fabric_freq_mhz: Fabric (Uncore) maximum frequency
|
||||
* @p1_fabric_freq_mhz: Fabric (Uncore) TDP frequency
|
||||
* @pn_fabric_freq_mhz: Fabric (Uncore) minimum efficiency frequency
|
||||
* @pm_fabric_freq_mhz: Fabric (Uncore) minimum frequency
|
||||
* @max_buckets: Maximum trl buckets
|
||||
* @max_trl_levels: Maximum trl levels
|
||||
* @bucket_core_counts[TRL_MAX_BUCKETS]: Number of cores per bucket
|
||||
* @trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]: maximum frequency
|
||||
* for a bucket and trl level
|
||||
*
|
||||
* Structure used to get information on frequencies and TDP for a SST-PP
|
||||
* level using ISST_IF_GET_PERF_LEVEL_INFO.
|
||||
*/
|
||||
struct isst_perf_level_data_info {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u16 level;
|
||||
__u16 tdp_ratio;
|
||||
__u16 base_freq_mhz;
|
||||
__u16 base_freq_avx2_mhz;
|
||||
__u16 base_freq_avx512_mhz;
|
||||
__u16 base_freq_amx_mhz;
|
||||
__u16 thermal_design_power_w;
|
||||
__u16 tjunction_max_c;
|
||||
__u16 max_memory_freq_mhz;
|
||||
__u16 cooling_type;
|
||||
__u16 p0_freq_mhz;
|
||||
__u16 p1_freq_mhz;
|
||||
__u16 pn_freq_mhz;
|
||||
__u16 pm_freq_mhz;
|
||||
__u16 p0_fabric_freq_mhz;
|
||||
__u16 p1_fabric_freq_mhz;
|
||||
__u16 pn_fabric_freq_mhz;
|
||||
__u16 pm_fabric_freq_mhz;
|
||||
__u16 max_buckets;
|
||||
__u16 max_trl_levels;
|
||||
__u16 bucket_core_counts[TRL_MAX_BUCKETS];
|
||||
__u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_perf_level_cpu_mask - Structure to get SST-PP level CPU mask
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @level: SST-PP level for which caller wants to get information
|
||||
* @punit_cpu_map: Set to 1 if the CPU number is punit numbering not
|
||||
* Linux CPU number. If 0 CPU buffer is copied to user space
|
||||
* supplied cpu_buffer of size cpu_buffer_size. Punit
|
||||
* cpu mask is copied to "mask" field.
|
||||
* @mask: cpu mask for this PP level (punit CPU numbering)
|
||||
* @cpu_buffer_size: size of cpu_buffer also used to return the copied CPU
|
||||
* buffer size.
|
||||
* @cpu_buffer: Buffer to copy CPU mask when punit_cpu_map is 0
|
||||
*
|
||||
* Structure used to get cpumask for a SST-PP level using
|
||||
* IOCTL ISST_IF_GET_PERF_LEVEL_CPU_MASK. Also used to get CPU mask for
|
||||
* IOCTL ISST_IF_GET_BASE_FREQ_CPU_MASK for SST-BF.
|
||||
*/
|
||||
struct isst_perf_level_cpu_mask {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 level;
|
||||
__u8 punit_cpu_map;
|
||||
__u64 mask;
|
||||
__u16 cpu_buffer_size;
|
||||
__s8 cpu_buffer[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_base_freq_info - Structure to get SST-BF frequencies
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @level: SST-PP level for which caller wants to get information
|
||||
* @high_base_freq_mhz: High priority CPU base frequency
|
||||
* @low_base_freq_mhz: Low priority CPU base frequency
|
||||
* @tjunction_max_c: Max junction temperature
|
||||
* @thermal_design_power_w: Thermal design power in watts
|
||||
*
|
||||
* Structure used to get SST-BF information using
|
||||
* IOCTL ISST_IF_GET_BASE_FREQ_INFO.
|
||||
*/
|
||||
struct isst_base_freq_info {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u16 level;
|
||||
__u16 high_base_freq_mhz;
|
||||
__u16 low_base_freq_mhz;
|
||||
__u16 tjunction_max_c;
|
||||
__u16 thermal_design_power_w;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_turbo_freq_info - Structure to get SST-TF frequencies
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @level: SST-PP level for which caller wants to get information
|
||||
* @max_clip_freqs: Maximum number of low priority core clipping frequencies
|
||||
* @lp_clip_freq_mhz: Clip frequencies per trl level
|
||||
* @bucket_core_counts: Maximum number of cores for a bucket
|
||||
* @trl_freq_mhz: Frequencies per trl level for each bucket
|
||||
*
|
||||
* Structure used to get SST-TF information using
|
||||
* IOCTL ISST_IF_GET_TURBO_FREQ_INFO.
|
||||
*/
|
||||
struct isst_turbo_freq_info {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u16 level;
|
||||
__u16 max_clip_freqs;
|
||||
__u16 max_buckets;
|
||||
__u16 max_trl_levels;
|
||||
__u16 lp_clip_freq_mhz[TRL_MAX_LEVELS];
|
||||
__u16 bucket_core_counts[TRL_MAX_BUCKETS];
|
||||
__u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS];
|
||||
};
|
||||
|
||||
#define ISST_IF_MAGIC 0xFE
|
||||
#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
|
||||
#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
|
||||
#define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *)
|
||||
#define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *)
|
||||
#define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *)
|
||||
|
||||
#define ISST_IF_COUNT_TPMI_INSTANCES _IOR(ISST_IF_MAGIC, 5, struct isst_tpmi_instance_count *)
|
||||
#define ISST_IF_CORE_POWER_STATE _IOWR(ISST_IF_MAGIC, 6, struct isst_core_power *)
|
||||
#define ISST_IF_CLOS_PARAM _IOWR(ISST_IF_MAGIC, 7, struct isst_clos_param *)
|
||||
#define ISST_IF_CLOS_ASSOC _IOWR(ISST_IF_MAGIC, 8, struct isst_if_clos_assoc_cmds *)
|
||||
|
||||
#define ISST_IF_PERF_LEVELS _IOWR(ISST_IF_MAGIC, 9, struct isst_perf_level_info *)
|
||||
#define ISST_IF_PERF_SET_LEVEL _IOW(ISST_IF_MAGIC, 10, struct isst_perf_level_control *)
|
||||
#define ISST_IF_PERF_SET_FEATURE _IOW(ISST_IF_MAGIC, 11, struct isst_perf_feature_control *)
|
||||
#define ISST_IF_GET_PERF_LEVEL_INFO _IOR(ISST_IF_MAGIC, 12, struct isst_perf_level_data_info *)
|
||||
#define ISST_IF_GET_PERF_LEVEL_CPU_MASK _IOR(ISST_IF_MAGIC, 13, struct isst_perf_level_cpu_mask *)
|
||||
#define ISST_IF_GET_BASE_FREQ_INFO _IOR(ISST_IF_MAGIC, 14, struct isst_base_freq_info *)
|
||||
#define ISST_IF_GET_BASE_FREQ_CPU_MASK _IOR(ISST_IF_MAGIC, 15, struct isst_perf_level_cpu_mask *)
|
||||
#define ISST_IF_GET_TURBO_FREQ_INFO _IOR(ISST_IF_MAGIC, 16, struct isst_turbo_freq_info *)
|
||||
|
||||
#endif
|
||||
|
@ -1 +1 @@
|
||||
intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o
|
||||
intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o isst-core-mbox.o isst-core-tpmi.o
|
||||
|
File diff suppressed because it is too large
Load Diff
1066
tools/power/x86/intel-speed-select/isst-core-mbox.c
Normal file
1066
tools/power/x86/intel-speed-select/isst-core-mbox.c
Normal file
File diff suppressed because it is too large
Load Diff
787
tools/power/x86/intel-speed-select/isst-core-tpmi.c
Normal file
787
tools/power/x86/intel-speed-select/isst-core-tpmi.c
Normal file
@ -0,0 +1,787 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel Speed Select -- Enumerate and control features for TPMI Interface
|
||||
* Copyright (c) 2022 Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/isst_if.h>
|
||||
#include "isst.h"
|
||||
|
||||
int tpmi_process_ioctl(int ioctl_no, void *info)
|
||||
{
|
||||
const char *pathname = "/dev/isst_interface";
|
||||
int fd;
|
||||
|
||||
if (is_debug_enabled()) {
|
||||
debug_printf("Issue IOCTL: ");
|
||||
switch (ioctl_no) {
|
||||
case ISST_IF_CORE_POWER_STATE:
|
||||
debug_printf("ISST_IF_CORE_POWER_STATE\n");
|
||||
break;
|
||||
case ISST_IF_CLOS_PARAM:
|
||||
debug_printf("ISST_IF_CLOS_PARAM\n");
|
||||
break;
|
||||
case ISST_IF_CLOS_ASSOC:
|
||||
debug_printf("ISST_IF_CLOS_ASSOC\n");
|
||||
break;
|
||||
case ISST_IF_PERF_LEVELS:
|
||||
debug_printf("ISST_IF_PERF_LEVELS\n");
|
||||
break;
|
||||
case ISST_IF_PERF_SET_LEVEL:
|
||||
debug_printf("ISST_IF_PERF_SET_LEVEL\n");
|
||||
break;
|
||||
case ISST_IF_PERF_SET_FEATURE:
|
||||
debug_printf("ISST_IF_PERF_SET_FEATURE\n");
|
||||
break;
|
||||
case ISST_IF_GET_PERF_LEVEL_INFO:
|
||||
debug_printf("ISST_IF_GET_PERF_LEVEL_INFO\n");
|
||||
break;
|
||||
case ISST_IF_GET_PERF_LEVEL_CPU_MASK:
|
||||
debug_printf("ISST_IF_GET_PERF_LEVEL_CPU_MASK\n");
|
||||
break;
|
||||
case ISST_IF_GET_BASE_FREQ_INFO:
|
||||
debug_printf("ISST_IF_GET_BASE_FREQ_INFO\n");
|
||||
break;
|
||||
case ISST_IF_GET_BASE_FREQ_CPU_MASK:
|
||||
debug_printf("ISST_IF_GET_BASE_FREQ_CPU_MASK\n");
|
||||
break;
|
||||
case ISST_IF_GET_TURBO_FREQ_INFO:
|
||||
debug_printf("ISST_IF_GET_TURBO_FREQ_INFO\n");
|
||||
break;
|
||||
case ISST_IF_COUNT_TPMI_INSTANCES:
|
||||
debug_printf("ISST_IF_COUNT_TPMI_INSTANCES\n");
|
||||
break;
|
||||
default:
|
||||
debug_printf("%d\n", ioctl_no);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fd = open(pathname, O_RDWR);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (ioctl(fd, ioctl_no, info) == -1) {
|
||||
debug_printf("IOCTL %d Failed\n", ioctl_no);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_disp_freq_multiplier(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tpmi_get_trl_max_levels(void)
|
||||
{
|
||||
return TRL_MAX_LEVELS;
|
||||
}
|
||||
|
||||
static char *tpmi_get_trl_level_name(int level)
|
||||
{
|
||||
switch (level) {
|
||||
case 0:
|
||||
return "level-0";
|
||||
case 1:
|
||||
return "level-1";
|
||||
case 2:
|
||||
return "level-2";
|
||||
case 3:
|
||||
return "level-3";
|
||||
case 4:
|
||||
return "level-4";
|
||||
case 5:
|
||||
return "level-5";
|
||||
case 6:
|
||||
return "level-6";
|
||||
case 7:
|
||||
return "level-7";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void tpmi_update_platform_param(enum isst_platform_param param, int value)
|
||||
{
|
||||
/* No params need to be updated for now */
|
||||
}
|
||||
|
||||
static int tpmi_is_punit_valid(struct isst_id *id)
|
||||
{
|
||||
struct isst_tpmi_instance_count info;
|
||||
int ret;
|
||||
|
||||
if (id->punit < 0)
|
||||
return 0;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
ret = tpmi_process_ioctl(ISST_IF_COUNT_TPMI_INSTANCES, &info);
|
||||
if (ret == -1)
|
||||
return 0;
|
||||
|
||||
if (info.valid_mask & BIT(id->punit))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap)
|
||||
{
|
||||
struct isst_core_power info;
|
||||
int ret;
|
||||
|
||||
info.get_set = 0;
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
*cp_state = info.enable;
|
||||
*cp_cap = info.supported;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpmi_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev)
|
||||
{
|
||||
struct isst_perf_level_info info;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
pkg_dev->version = info.feature_rev;
|
||||
pkg_dev->levels = info.max_level;
|
||||
pkg_dev->locked = info.locked;
|
||||
pkg_dev->current_level = info.current_level;
|
||||
pkg_dev->locked = info.locked;
|
||||
pkg_dev->enabled = info.enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_ctdp_control(struct isst_id *id, int config_index,
|
||||
struct isst_pkg_ctdp_level_info *ctdp_level)
|
||||
{
|
||||
struct isst_core_power core_power_info;
|
||||
struct isst_perf_level_info info;
|
||||
int level_mask;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
|
||||
if (config_index != 0xff)
|
||||
level_mask = 1 << config_index;
|
||||
else
|
||||
level_mask = config_index;
|
||||
|
||||
if (!(info.level_mask & level_mask))
|
||||
return -1;
|
||||
|
||||
ctdp_level->fact_support = info.sst_tf_support;
|
||||
ctdp_level->pbf_support = info.sst_bf_support;
|
||||
ctdp_level->fact_enabled = !!(info.feature_state & BIT(1));
|
||||
ctdp_level->pbf_enabled = !!(info.feature_state & BIT(0));
|
||||
|
||||
core_power_info.get_set = 0;
|
||||
core_power_info.socket_id = id->pkg;
|
||||
core_power_info.power_domain_id = id->punit;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &core_power_info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
ctdp_level->sst_cp_support = core_power_info.supported;
|
||||
ctdp_level->sst_cp_enabled = core_power_info.enable;
|
||||
|
||||
debug_printf
|
||||
("cpu:%d CONFIG_TDP_GET_TDP_CONTROL fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n",
|
||||
id->cpu, ctdp_level->fact_support, ctdp_level->pbf_support,
|
||||
ctdp_level->fact_enabled, ctdp_level->pbf_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_tdp_info(struct isst_id *id, int config_index,
|
||||
struct isst_pkg_ctdp_level_info *ctdp_level)
|
||||
{
|
||||
struct isst_perf_level_data_info info;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = config_index;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
ctdp_level->pkg_tdp = info.thermal_design_power_w;
|
||||
ctdp_level->tdp_ratio = info.tdp_ratio;
|
||||
ctdp_level->sse_p1 = info.base_freq_mhz;
|
||||
ctdp_level->avx2_p1 = info.base_freq_avx2_mhz;
|
||||
ctdp_level->avx512_p1 = info.base_freq_avx512_mhz;
|
||||
ctdp_level->amx_p1 = info.base_freq_amx_mhz;
|
||||
|
||||
ctdp_level->t_proc_hot = info.tjunction_max_c;
|
||||
ctdp_level->mem_freq = info.max_memory_freq_mhz;
|
||||
ctdp_level->cooling_type = info.cooling_type;
|
||||
|
||||
ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz;
|
||||
ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz;
|
||||
ctdp_level->uncore_pm = info.pm_fabric_freq_mhz;
|
||||
|
||||
debug_printf
|
||||
("cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO tdp_ratio:%d pkg_tdp:%d ctdp_level->t_proc_hot:%d\n",
|
||||
id->cpu, config_index, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp,
|
||||
ctdp_level->t_proc_hot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_pwr_info(struct isst_id *id, int config_index,
|
||||
struct isst_pkg_ctdp_level_info *ctdp_level)
|
||||
{
|
||||
/* TBD */
|
||||
ctdp_level->pkg_max_power = 0;
|
||||
ctdp_level->pkg_min_power = 0;
|
||||
|
||||
debug_printf
|
||||
("cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO pkg_max_power:%d pkg_min_power:%d\n",
|
||||
id->cpu, config_index, ctdp_level->pkg_max_power,
|
||||
ctdp_level->pkg_min_power);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpmi_get_coremask_info(struct isst_id *id, int config_index,
|
||||
struct isst_pkg_ctdp_level_info *ctdp_level)
|
||||
{
|
||||
struct isst_perf_level_cpu_mask info;
|
||||
int ret, cpu_count;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = config_index;
|
||||
info.punit_cpu_map = 1;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_CPU_MASK, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
set_cpu_mask_from_punit_coremask(id, info.mask,
|
||||
ctdp_level->core_cpumask_size,
|
||||
ctdp_level->core_cpumask, &cpu_count);
|
||||
ctdp_level->cpu_count = cpu_count;
|
||||
|
||||
debug_printf("cpu:%d ctdp:%d core_mask ino cpu count:%d\n",
|
||||
id->cpu, config_index, ctdp_level->cpu_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_get_trls(struct isst_id *id, int config_index,
|
||||
struct isst_pkg_ctdp_level_info *ctdp_level)
|
||||
{
|
||||
struct isst_perf_level_data_info info;
|
||||
int ret, i, j;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = config_index;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
if (info.max_buckets > TRL_MAX_BUCKETS)
|
||||
info.max_buckets = TRL_MAX_BUCKETS;
|
||||
|
||||
if (info.max_trl_levels > TRL_MAX_LEVELS)
|
||||
info.max_trl_levels = TRL_MAX_LEVELS;
|
||||
|
||||
for (i = 0; i < info.max_trl_levels; ++i)
|
||||
for (j = 0; j < info.max_buckets; ++j)
|
||||
ctdp_level->trl_ratios[i][j] = info.trl_freq_mhz[i][j];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_get_trl(struct isst_id *id, int level, int config_index,
|
||||
int *trl)
|
||||
{
|
||||
struct isst_pkg_ctdp_level_info ctdp_level;
|
||||
int ret, i;
|
||||
|
||||
ret = tpmi_get_get_trls(id, config_index, &ctdp_level);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* FIX ME: Just return for level 0 */
|
||||
for (i = 0; i < 8; ++i)
|
||||
trl[i] = ctdp_level.trl_ratios[0][i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_trl_bucket_info(struct isst_id *id, int config_index,
|
||||
unsigned long long *buckets_info)
|
||||
{
|
||||
struct isst_perf_level_data_info info;
|
||||
unsigned char *mask = (unsigned char *)buckets_info;
|
||||
int ret, i;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = config_index;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
if (info.max_buckets > TRL_MAX_BUCKETS)
|
||||
info.max_buckets = TRL_MAX_BUCKETS;
|
||||
|
||||
for (i = 0; i < info.max_buckets; ++i)
|
||||
mask[i] = info.bucket_core_counts[i];
|
||||
|
||||
debug_printf("cpu:%d TRL bucket info: 0x%llx\n", id->cpu,
|
||||
*buckets_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_set_tdp_level(struct isst_id *id, int tdp_level)
|
||||
{
|
||||
struct isst_perf_level_control info;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = tdp_level;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_PERF_SET_LEVEL, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _pbf_get_coremask_info(struct isst_id *id, int config_index,
|
||||
struct isst_pbf_info *pbf_info)
|
||||
{
|
||||
struct isst_perf_level_cpu_mask info;
|
||||
int ret, cpu_count;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = config_index;
|
||||
info.punit_cpu_map = 1;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_CPU_MASK, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
set_cpu_mask_from_punit_coremask(id, info.mask,
|
||||
pbf_info->core_cpumask_size,
|
||||
pbf_info->core_cpumask, &cpu_count);
|
||||
|
||||
debug_printf("cpu:%d ctdp:%d pbf core_mask info cpu count:%d\n",
|
||||
id->cpu, config_index, cpu_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_pbf_info(struct isst_id *id, int level,
|
||||
struct isst_pbf_info *pbf_info)
|
||||
{
|
||||
struct isst_base_freq_info info;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = level;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_INFO, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
pbf_info->p1_low = info.low_base_freq_mhz;
|
||||
pbf_info->p1_high = info.high_base_freq_mhz;
|
||||
pbf_info->tdp = info.thermal_design_power_w;
|
||||
pbf_info->t_prochot = info.tjunction_max_c;
|
||||
|
||||
debug_printf("cpu:%d ctdp:%d pbf info:%d:%d:%d:%d\n",
|
||||
id->cpu, level, pbf_info->p1_low, pbf_info->p1_high,
|
||||
pbf_info->tdp, pbf_info->t_prochot);
|
||||
|
||||
return _pbf_get_coremask_info(id, level, pbf_info);
|
||||
}
|
||||
|
||||
static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable)
|
||||
{
|
||||
struct isst_pkg_ctdp pkg_dev;
|
||||
struct isst_pkg_ctdp_level_info ctdp_level;
|
||||
int current_level;
|
||||
struct isst_perf_feature_control info;
|
||||
int ret;
|
||||
|
||||
ret = isst_get_ctdp_levels(id, &pkg_dev);
|
||||
if (ret)
|
||||
debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu);
|
||||
|
||||
current_level = pkg_dev.current_level;
|
||||
|
||||
ret = isst_get_ctdp_control(id, current_level, &ctdp_level);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
|
||||
info.feature = 0;
|
||||
|
||||
if (pbf) {
|
||||
if (ctdp_level.fact_enabled)
|
||||
info.feature |= BIT(1);
|
||||
|
||||
if (enable)
|
||||
info.feature |= BIT(0);
|
||||
else
|
||||
info.feature &= ~BIT(0);
|
||||
} else {
|
||||
|
||||
if (enable && !ctdp_level.sst_cp_enabled)
|
||||
isst_display_error_info_message(0,
|
||||
"Make sure to execute before: core-power enable",
|
||||
0, 0);
|
||||
|
||||
if (ctdp_level.pbf_enabled)
|
||||
info.feature |= BIT(0);
|
||||
|
||||
if (enable)
|
||||
info.feature |= BIT(1);
|
||||
else
|
||||
info.feature &= ~BIT(1);
|
||||
}
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_PERF_SET_FEATURE, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_get_fact_info(struct isst_id *id, int level, int fact_bucket,
|
||||
struct isst_fact_info *fact_info)
|
||||
{
|
||||
struct isst_turbo_freq_info info;
|
||||
int i, j;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = level;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_TURBO_FREQ_INFO, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < info.max_clip_freqs; ++i)
|
||||
fact_info->lp_ratios[i] = info.lp_clip_freq_mhz[i];
|
||||
|
||||
if (info.max_buckets > TRL_MAX_BUCKETS)
|
||||
info.max_buckets = TRL_MAX_BUCKETS;
|
||||
|
||||
if (info.max_trl_levels > TRL_MAX_LEVELS)
|
||||
info.max_trl_levels = TRL_MAX_LEVELS;
|
||||
|
||||
for (i = 0; i < info.max_trl_levels; ++i) {
|
||||
for (j = 0; j < info.max_buckets; ++j)
|
||||
fact_info->bucket_info[j].hp_ratios[i] =
|
||||
info.trl_freq_mhz[i][j];
|
||||
}
|
||||
|
||||
for (i = 0; i < info.max_buckets; ++i)
|
||||
fact_info->bucket_info[i].hp_cores = info.bucket_core_counts[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_uncore_min_max(struct isst_id *id, int max, int freq)
|
||||
{
|
||||
DIR *dir;
|
||||
FILE *filep;
|
||||
struct dirent *entry;
|
||||
char buffer[512];
|
||||
unsigned int tmp_id;
|
||||
int ret;
|
||||
|
||||
dir = opendir("/sys/devices/system/cpu/intel_uncore_frequency/");
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL ) {
|
||||
/* Check domain_id */
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"/sys/devices/system/cpu/intel_uncore_frequency/%s/domain_id", entry->d_name);
|
||||
|
||||
filep = fopen(buffer, "r");
|
||||
if (!filep)
|
||||
goto end;
|
||||
|
||||
ret = fscanf(filep, "%u", &tmp_id);
|
||||
fclose(filep);
|
||||
if (ret != 1)
|
||||
goto end;
|
||||
|
||||
if (tmp_id != id->punit)
|
||||
continue;
|
||||
|
||||
/* Check package_id */
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"/sys/devices/system/cpu/intel_uncore_frequency/%s/package_id", entry->d_name);
|
||||
|
||||
filep = fopen(buffer, "r");
|
||||
if (!filep)
|
||||
goto end;
|
||||
|
||||
ret = fscanf(filep, "%u", &tmp_id);
|
||||
fclose(filep);
|
||||
|
||||
if (ret != 1)
|
||||
goto end;
|
||||
|
||||
if (tmp_id != id->pkg)
|
||||
continue;
|
||||
|
||||
/* Found the right sysfs path, adjust and quit */
|
||||
if (max)
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"/sys/devices/system/cpu/intel_uncore_frequency/%s/max_freq_khz", entry->d_name);
|
||||
else
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"/sys/devices/system/cpu/intel_uncore_frequency/%s/min_freq_khz", entry->d_name);
|
||||
|
||||
filep = fopen(buffer, "w");
|
||||
if (!filep)
|
||||
goto end;
|
||||
|
||||
fprintf(filep, "%d\n", freq);
|
||||
fclose(filep);
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static void tpmi_adjust_uncore_freq(struct isst_id *id, int config_index,
|
||||
struct isst_pkg_ctdp_level_info *ctdp_level)
|
||||
{
|
||||
struct isst_perf_level_data_info info;
|
||||
int ret;
|
||||
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.level = config_index;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info);
|
||||
if (ret == -1)
|
||||
return;
|
||||
|
||||
ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz;
|
||||
ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz;
|
||||
ctdp_level->uncore_pm = info.pm_fabric_freq_mhz;
|
||||
|
||||
if (ctdp_level->uncore_pm)
|
||||
_set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000);
|
||||
|
||||
if (ctdp_level->uncore_p0)
|
||||
_set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int tpmi_get_clos_information(struct isst_id *id, int *enable, int *type)
|
||||
{
|
||||
struct isst_core_power info;
|
||||
int ret;
|
||||
|
||||
info.get_set = 0;
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
*enable = info.enable;
|
||||
*type = info.priority_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos,
|
||||
int priority_type)
|
||||
{
|
||||
struct isst_core_power info;
|
||||
int ret;
|
||||
|
||||
info.get_set = 1;
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.enable = enable_clos;
|
||||
info.priority_type = priority_type;
|
||||
ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpmi_pm_get_clos(struct isst_id *id, int clos,
|
||||
struct isst_clos_config *clos_config)
|
||||
{
|
||||
struct isst_clos_param info;
|
||||
int ret;
|
||||
|
||||
info.get_set = 0;
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.clos = clos;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
clos_config->epp = 0;
|
||||
clos_config->clos_prop_prio = info.prop_prio;
|
||||
clos_config->clos_min = info.min_freq_mhz;
|
||||
clos_config->clos_max = info.max_freq_mhz;
|
||||
clos_config->clos_desired = 0;
|
||||
|
||||
debug_printf("cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos,
|
||||
clos_config->clos_min, clos_config->clos_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpmi_set_clos(struct isst_id *id, int clos,
|
||||
struct isst_clos_config *clos_config)
|
||||
{
|
||||
struct isst_clos_param info;
|
||||
int ret;
|
||||
|
||||
info.get_set = 1;
|
||||
info.socket_id = id->pkg;
|
||||
info.power_domain_id = id->punit;
|
||||
info.clos = clos;
|
||||
info.prop_prio = clos_config->clos_prop_prio;
|
||||
|
||||
info.min_freq_mhz = clos_config->clos_min;
|
||||
info.max_freq_mhz = clos_config->clos_max;
|
||||
|
||||
if (info.min_freq_mhz <= 0xff)
|
||||
info.min_freq_mhz *= 100;
|
||||
if (info.max_freq_mhz <= 0xff)
|
||||
info.max_freq_mhz *= 100;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
debug_printf("set cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos,
|
||||
clos_config->clos_min, clos_config->clos_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_clos_get_assoc_status(struct isst_id *id, int *clos_id)
|
||||
{
|
||||
struct isst_if_clos_assoc_cmds assoc_cmds;
|
||||
int ret;
|
||||
|
||||
assoc_cmds.cmd_count = 1;
|
||||
assoc_cmds.get_set = 0;
|
||||
assoc_cmds.punit_cpu_map = 1;
|
||||
assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu);
|
||||
assoc_cmds.assoc_info[0].socket_id = id->pkg;
|
||||
assoc_cmds.assoc_info[0].power_domain_id = id->punit;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
*clos_id = assoc_cmds.assoc_info[0].clos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_clos_associate(struct isst_id *id, int clos_id)
|
||||
{
|
||||
struct isst_if_clos_assoc_cmds assoc_cmds;
|
||||
int ret;
|
||||
|
||||
assoc_cmds.cmd_count = 1;
|
||||
assoc_cmds.get_set = 1;
|
||||
assoc_cmds.punit_cpu_map = 1;
|
||||
assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu);
|
||||
assoc_cmds.assoc_info[0].clos = clos_id;
|
||||
assoc_cmds.assoc_info[0].socket_id = id->pkg;
|
||||
assoc_cmds.assoc_info[0].power_domain_id = id->punit;
|
||||
|
||||
ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct isst_platform_ops tpmi_ops = {
|
||||
.get_disp_freq_multiplier = tpmi_get_disp_freq_multiplier,
|
||||
.get_trl_max_levels = tpmi_get_trl_max_levels,
|
||||
.get_trl_level_name = tpmi_get_trl_level_name,
|
||||
.update_platform_param = tpmi_update_platform_param,
|
||||
.is_punit_valid = tpmi_is_punit_valid,
|
||||
.read_pm_config = tpmi_read_pm_config,
|
||||
.get_config_levels = tpmi_get_config_levels,
|
||||
.get_ctdp_control = tpmi_get_ctdp_control,
|
||||
.get_tdp_info = tpmi_get_tdp_info,
|
||||
.get_pwr_info = tpmi_get_pwr_info,
|
||||
.get_coremask_info = tpmi_get_coremask_info,
|
||||
.get_get_trl = tpmi_get_get_trl,
|
||||
.get_get_trls = tpmi_get_get_trls,
|
||||
.get_trl_bucket_info = tpmi_get_trl_bucket_info,
|
||||
.set_tdp_level = tpmi_set_tdp_level,
|
||||
.get_pbf_info = tpmi_get_pbf_info,
|
||||
.set_pbf_fact_status = tpmi_set_pbf_fact_status,
|
||||
.get_fact_info = tpmi_get_fact_info,
|
||||
.adjust_uncore_freq = tpmi_adjust_uncore_freq,
|
||||
.get_clos_information = tpmi_get_clos_information,
|
||||
.pm_qos_config = tpmi_pm_qos_config,
|
||||
.pm_get_clos = tpmi_pm_get_clos,
|
||||
.set_clos = tpmi_set_clos,
|
||||
.clos_get_assoc_status = tpmi_clos_get_assoc_status,
|
||||
.clos_associate = tpmi_clos_associate,
|
||||
};
|
||||
|
||||
struct isst_platform_ops *tpmi_get_platform_ops(void)
|
||||
{
|
||||
return &tpmi_ops;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user