diff options
Diffstat (limited to 'SOURCES/asus-linux.patch')
-rw-r--r-- | SOURCES/asus-linux.patch | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/SOURCES/asus-linux.patch b/SOURCES/asus-linux.patch new file mode 100644 index 0000000..1580454 --- /dev/null +++ b/SOURCES/asus-linux.patch @@ -0,0 +1,438 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jan200101 <sentrycraft123@gmail.com> +Date: Wed, 8 Mar 2023 08:26:17 +0100 +Subject: [PATCH] asus-linux This patch adds support for the tablet mode switch + sensors on convertible devices where that sensor is managed by AMD SFH, like + the Asus Flow X13 and the Lenovo ThinkPad L13 Yoga Gen2 (AMD). + +Co-developed-by: Ivan Dovgal <iv.dovg@gmail.com> +Signed-off-by: Ivan Dovgal <iv.dovg@gmail.com> +Co-developed-by: Luke D. Jones <luke@ljones.dev> +Signed-off-by: Luke D. Jones <luke@ljones.dev> +Signed-off-by: Adrian Freund <adrian@freund.io> +--- +v2: +* Fixed build warning reported by kernel test robot <lkp@intel.com> +Signed-off-by: Jan200101 <sentrycraft123@gmail.com> +--- + drivers/hid/amd-sfh-hid/amd_sfh_client.c | 2 + + drivers/hid/amd-sfh-hid/amd_sfh_hid.h | 2 +- + drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 4 + + drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 1 + + .../hid_descriptor/amd_sfh_hid_desc.c | 27 ++++++ + .../hid_descriptor/amd_sfh_hid_desc.h | 7 ++ + .../hid_descriptor/amd_sfh_hid_report_desc.h | 21 ++++ + drivers/pci/controller/vmd.c | 96 ++++++++++++++----- + drivers/pci/pcie/aspm.c | 54 +++++++++++ + include/linux/pci.h | 7 ++ + 10 files changed, 194 insertions(+), 27 deletions(-) + +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c +index c751d12f5..690be680e 100644 +--- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c +@@ -146,6 +146,8 @@ static const char *get_sensor_name(int idx) + return "gyroscope"; + case mag_idx: + return "magnetometer"; ++ case tms_idx: ++ return "tablet-mode-switch"; + case als_idx: + return "ALS"; + case HPD_IDX: +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h +index 528036892..97296f587 100644 +--- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h +@@ -11,7 +11,7 @@ + #ifndef AMDSFH_HID_H + #define AMDSFH_HID_H + +-#define MAX_HID_DEVICES 5 ++#define MAX_HID_DEVICES 6 + #define AMD_SFH_HID_VENDOR 0x1022 + #define AMD_SFH_HID_PRODUCT 0x0001 + +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +index 47774b9ab..cfda797f0 100644 +--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +@@ -27,6 +27,7 @@ + #define ACEL_EN BIT(0) + #define GYRO_EN BIT(1) + #define MAGNO_EN BIT(2) ++#define TMS_EN BIT(15) + #define HPD_EN BIT(16) + #define ALS_EN BIT(19) + +@@ -227,6 +228,9 @@ int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) + if (MAGNO_EN & activestatus) + sensor_id[num_of_sensors++] = mag_idx; + ++ if (TMS_EN & activestatus) ++ sensor_id[num_of_sensors++] = tms_idx; ++ + if (ALS_EN & activestatus) + sensor_id[num_of_sensors++] = als_idx; + +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +index dfb7cabd8..e18ceee9e 100644 +--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +@@ -78,6 +78,7 @@ enum sensor_idx { + accel_idx = 0, + gyro_idx = 1, + mag_idx = 2, ++ tms_idx = 15, + als_idx = 19 + }; + +diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c +index f9a8c02d5..181973f35 100644 +--- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c ++++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c +@@ -47,6 +47,11 @@ static int get_report_descriptor(int sensor_idx, u8 *rep_desc) + memcpy(rep_desc, comp3_report_descriptor, + sizeof(comp3_report_descriptor)); + break; ++ case tms_idx: /* tablet mode switch */ ++ memset(rep_desc, 0, sizeof(tms_report_descriptor)); ++ memcpy(rep_desc, tms_report_descriptor, ++ sizeof(tms_report_descriptor)); ++ break; + case als_idx: /* ambient light sensor */ + memset(rep_desc, 0, sizeof(als_report_descriptor)); + memcpy(rep_desc, als_report_descriptor, +@@ -96,6 +101,16 @@ static u32 get_descr_sz(int sensor_idx, int descriptor_name) + return sizeof(struct magno_feature_report); + } + break; ++ case tms_idx: ++ switch (descriptor_name) { ++ case descr_size: ++ return sizeof(tms_report_descriptor); ++ case input_size: ++ return sizeof(struct tms_input_report); ++ case feature_size: ++ return sizeof(struct tms_feature_report); ++ } ++ break; + case als_idx: + switch (descriptor_name) { + case descr_size: +@@ -138,6 +153,7 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report) + struct accel3_feature_report acc_feature; + struct gyro_feature_report gyro_feature; + struct magno_feature_report magno_feature; ++ struct tms_feature_report tms_feature; + struct hpd_feature_report hpd_feature; + struct als_feature_report als_feature; + u8 report_size = 0; +@@ -173,6 +189,11 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report) + memcpy(feature_report, &magno_feature, sizeof(magno_feature)); + report_size = sizeof(magno_feature); + break; ++ case tms_idx: /* tablet mode switch */ ++ get_common_features(&tms_feature.common_property, report_id); ++ memcpy(feature_report, &tms_feature, sizeof(tms_feature)); ++ report_size = sizeof(tms_feature); ++ break; + case als_idx: /* ambient light sensor */ + get_common_features(&als_feature.common_property, report_id); + als_feature.als_change_sesnitivity = HID_DEFAULT_SENSITIVITY; +@@ -211,6 +232,7 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, + struct accel3_input_report acc_input; + struct gyro_input_report gyro_input; + struct hpd_input_report hpd_input; ++ struct tms_input_report tms_input; + struct als_input_report als_input; + struct hpd_status hpdstatus; + u8 report_size = 0; +@@ -244,6 +266,11 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, + memcpy(input_report, &magno_input, sizeof(magno_input)); + report_size = sizeof(magno_input); + break; ++ case tms_idx: /* tablet mode switch */ ++ get_common_inputs(&tms_input.common_property, report_id); ++ report_size = sizeof(tms_input); ++ memcpy(input_report, &tms_input, sizeof(tms_input)); ++ break; + case als_idx: /* Als */ + get_common_inputs(&als_input.common_property, report_id); + /* For ALS ,V2 Platforms uses C2P_MSG5 register instead of DRAM access method */ +diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h +index ebd55675e..b22068a47 100644 +--- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h ++++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h +@@ -111,4 +111,11 @@ struct hpd_input_report { + u8 human_presence; + } __packed; + ++struct tms_feature_report { ++ struct common_feature_property common_property; ++} __packed; ++ ++struct tms_input_report { ++ struct common_input_property common_property; ++} __packed; + #endif +diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h +index 697f2791e..96cbc1e5b 100644 +--- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h ++++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h +@@ -644,6 +644,27 @@ static const u8 als_report_descriptor[] = { + 0xC0 /* HID end collection */ + }; + ++ ++/* TABLET MODE SWITCH */ ++__maybe_unused // Used by sfh1.0, but not yet implemented in sfh1.1 ++static const u8 tms_report_descriptor[] = { ++0x06, 0x43, 0xFF, // Usage Page (Vendor Defined 0xFF43) ++0x0A, 0x02, 0x02, // Usage (0x0202) ++0xA1, 0x01, // Collection (Application) ++0x85, 0x11, // Report ID (17) ++0x15, 0x00, // Logical Minimum (0) ++0x25, 0x01, // Logical Maximum (1) ++0x35, 0x00, // Physical Minimum (0) ++0x45, 0x01, // Physical Maximum (1) ++0x65, 0x00, // Unit (None) ++0x55, 0x00, // Unit Exponent (0) ++0x75, 0x01, // Report Size (1) ++0x95, 0x98, // Report Count (-104) ++0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) ++0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) ++0xC1, 0x00, // End Collection ++}; ++ + /* BIOMETRIC PRESENCE*/ + static const u8 hpd_report_descriptor[] = { + 0x05, 0x20, /* Usage page */ +diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c +index 769eedeb8..900bf82dc 100644 +--- a/drivers/pci/controller/vmd.c ++++ b/drivers/pci/controller/vmd.c +@@ -66,8 +66,22 @@ enum vmd_features { + * interrupt handling. + */ + VMD_FEAT_CAN_BYPASS_MSI_REMAP = (1 << 4), ++ ++ /* ++ * Enable ASPM on the PCIE root ports and set the default LTR of the ++ * storage devices on platforms where these values are not configured by ++ * BIOS. This is needed for laptops, which require these settings for ++ * proper power management of the SoC. ++ */ ++ VMD_FEAT_BIOS_PM_QUIRK = (1 << 5), + }; + ++#define VMD_FEATS_CLIENT (VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | \ ++ VMD_FEAT_HAS_BUS_RESTRICTIONS | \ ++ VMD_FEAT_OFFSET_FIRST_VECTOR) ++ ++#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */ ++ + static DEFINE_IDA(vmd_instance_ida); + + /* +@@ -709,6 +723,46 @@ static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge, + vmd_bridge->native_dpc = root_bridge->native_dpc; + } + ++/* ++ * Enable ASPM and LTR settings on devices that aren't configured by BIOS. ++ */ ++static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) ++{ ++ unsigned long features = *(unsigned long *)userdata; ++ u16 ltr = VMD_BIOS_PM_QUIRK_LTR; ++ u32 ltr_reg; ++ int pos; ++ ++ if (!(features & VMD_FEAT_BIOS_PM_QUIRK)) ++ return 0; ++ ++ pci_enable_link_state(pdev, PCIE_LINK_STATE_ALL); ++ ++ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR); ++ if (!pos) ++ return 0; ++ ++ /* ++ * Skip if the max snoop LTR is non-zero, indicating BIOS has set it ++ * so the LTR quirk is not needed. ++ */ ++ pci_read_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, <r_reg); ++ if (!!(ltr_reg & (PCI_LTR_VALUE_MASK | PCI_LTR_SCALE_MASK))) ++ return 0; ++ ++ /* ++ * Set the default values to the maximum required by the platform to ++ * allow the deepest power management savings. Write as a DWORD where ++ * the lower word is the max snoop latency and the upper word is the ++ * max non-snoop latency. ++ */ ++ ltr_reg = (ltr << 16) | ltr; ++ pci_write_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, ltr_reg); ++ pci_info(pdev, "VMD: Default LTR value set by driver\n"); ++ ++ return 0; ++} ++ + static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) + { + struct pci_sysdata *sd = &vmd->sysdata; +@@ -881,6 +935,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) + + pci_assign_unassigned_bus_resources(vmd->bus); + ++ pci_walk_bus(vmd->bus, vmd_pm_enable_quirk, &features); ++ + /* + * VMD root buses are virtual and don't return true on pci_is_pcie() + * and will fail pcie_bus_configure_settings() early. It can instead be +@@ -1017,36 +1073,24 @@ static int vmd_resume(struct device *dev) + static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume); + + static const struct pci_device_id vmd_ids[] = { +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D), ++ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_201D), + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), ++ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | + VMD_FEAT_HAS_BUS_RESTRICTIONS | + VMD_FEAT_CAN_BYPASS_MSI_REMAP,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f), +- .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +- VMD_FEAT_HAS_BUS_RESTRICTIONS | +- VMD_FEAT_OFFSET_FIRST_VECTOR,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d), +- .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +- VMD_FEAT_HAS_BUS_RESTRICTIONS | +- VMD_FEAT_OFFSET_FIRST_VECTOR,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa77f), +- .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +- VMD_FEAT_HAS_BUS_RESTRICTIONS | +- VMD_FEAT_OFFSET_FIRST_VECTOR,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7d0b), +- .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +- VMD_FEAT_HAS_BUS_RESTRICTIONS | +- VMD_FEAT_OFFSET_FIRST_VECTOR,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xad0b), +- .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +- VMD_FEAT_HAS_BUS_RESTRICTIONS | +- VMD_FEAT_OFFSET_FIRST_VECTOR,}, +- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), +- .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +- VMD_FEAT_HAS_BUS_RESTRICTIONS | +- VMD_FEAT_OFFSET_FIRST_VECTOR,}, ++ {PCI_VDEVICE(INTEL, 0x467f), ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_BIOS_PM_QUIRK,}, ++ {PCI_VDEVICE(INTEL, 0x4c3d), ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_BIOS_PM_QUIRK,}, ++ {PCI_VDEVICE(INTEL, 0xa77f), ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_BIOS_PM_QUIRK,}, ++ {PCI_VDEVICE(INTEL, 0x7d0b), ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_BIOS_PM_QUIRK,}, ++ {PCI_VDEVICE(INTEL, 0xad0b), ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_BIOS_PM_QUIRK,}, ++ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_BIOS_PM_QUIRK,}, + {0,} + }; + MODULE_DEVICE_TABLE(pci, vmd_ids); +diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c +index 4b4184563..66d7514ca 100644 +--- a/drivers/pci/pcie/aspm.c ++++ b/drivers/pci/pcie/aspm.c +@@ -1138,6 +1138,60 @@ int pci_disable_link_state(struct pci_dev *pdev, int state) + } + EXPORT_SYMBOL(pci_disable_link_state); + ++/** ++ * pci_enable_link_state - Clear and set the default device link state so that ++ * the link may be allowed to enter the specified states. Note that if the ++ * BIOS didn't grant ASPM control to the OS, this does nothing because we can't ++ * touch the LNKCTL register. Also note that this does not enable states ++ * disabled by pci_disable_link_state(). Return 0 or a negative errno. ++ * ++ * @pdev: PCI device ++ * @state: Mask of ASPM link states to enable ++ */ ++int pci_enable_link_state(struct pci_dev *pdev, int state) ++{ ++ struct pcie_link_state *link = pcie_aspm_get_link(pdev); ++ ++ if (!link) ++ return -EINVAL; ++ /* ++ * A driver requested that ASPM be enabled on this device, but ++ * if we don't have permission to manage ASPM (e.g., on ACPI ++ * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and ++ * the _OSC method), we can't honor that request. ++ */ ++ if (aspm_disabled) { ++ pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n"); ++ return -EPERM; ++ } ++ ++ down_read(&pci_bus_sem); ++ mutex_lock(&aspm_lock); ++ link->aspm_default = 0; ++ if (state & PCIE_LINK_STATE_L0S) ++ link->aspm_default |= ASPM_STATE_L0S; ++ if (state & PCIE_LINK_STATE_L1) ++ /* L1 PM substates require L1 */ ++ link->aspm_default |= ASPM_STATE_L1 | ASPM_STATE_L1SS; ++ if (state & PCIE_LINK_STATE_L1_1) ++ link->aspm_default |= ASPM_STATE_L1_1; ++ if (state & PCIE_LINK_STATE_L1_2) ++ link->aspm_default |= ASPM_STATE_L1_2; ++ if (state & PCIE_LINK_STATE_L1_1_PCIPM) ++ link->aspm_default |= ASPM_STATE_L1_1_PCIPM; ++ if (state & PCIE_LINK_STATE_L1_2_PCIPM) ++ link->aspm_default |= ASPM_STATE_L1_2_PCIPM; ++ pcie_config_aspm_link(link, policy_to_aspm_state(link)); ++ ++ link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0; ++ pcie_set_clkpm(link, policy_to_clkpm_state(link)); ++ mutex_unlock(&aspm_lock); ++ up_read(&pci_bus_sem); ++ ++ return 0; ++} ++EXPORT_SYMBOL(pci_enable_link_state); ++ + static int pcie_aspm_set_policy(const char *val, + const struct kernel_param *kp) + { +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 2bda4a4e4..8c35f15e6 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1651,10 +1651,15 @@ extern bool pcie_ports_native; + #define PCIE_LINK_STATE_L1_2 BIT(4) + #define PCIE_LINK_STATE_L1_1_PCIPM BIT(5) + #define PCIE_LINK_STATE_L1_2_PCIPM BIT(6) ++#define PCIE_LINK_STATE_ALL (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |\ ++ PCIE_LINK_STATE_CLKPM | PCIE_LINK_STATE_L1_1 |\ ++ PCIE_LINK_STATE_L1_2 | PCIE_LINK_STATE_L1_1_PCIPM |\ ++ PCIE_LINK_STATE_L1_2_PCIPM) + + #ifdef CONFIG_PCIEASPM + int pci_disable_link_state(struct pci_dev *pdev, int state); + int pci_disable_link_state_locked(struct pci_dev *pdev, int state); ++int pci_enable_link_state(struct pci_dev *pdev, int state); + void pcie_no_aspm(void); + bool pcie_aspm_support_enabled(void); + bool pcie_aspm_enabled(struct pci_dev *pdev); +@@ -1663,6 +1668,8 @@ static inline int pci_disable_link_state(struct pci_dev *pdev, int state) + { return 0; } + static inline int pci_disable_link_state_locked(struct pci_dev *pdev, int state) + { return 0; } ++static inline int pci_enable_link_state(struct pci_dev *pdev, int state) ++{ return 0; } + static inline void pcie_no_aspm(void) { } + static inline bool pcie_aspm_support_enabled(void) { return false; } + static inline bool pcie_aspm_enabled(struct pci_dev *pdev) { return false; } |