Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+9641 -3909
+14 -3
Documentation/ABI/testing/sysfs-platform-intel-ifs
··· 1 + Device instance to test mapping 2 + intel_ifs_0 -> Scan Test 3 + intel_ifs_1 -> Array BIST test 4 + 1 5 What: /sys/devices/virtual/misc/intel_ifs_<N>/run_test 2 6 Date: Nov 16 2022 3 7 KernelVersion: 6.2 ··· 12 8 completes the test for the core containing that thread. 13 9 Example: to test the core containing cpu5: echo 5 > 14 10 /sys/devices/virtual/misc/intel_ifs_<N>/run_test 11 + Devices: all 15 12 16 13 What: /sys/devices/virtual/misc/intel_ifs_<N>/status 17 14 Date: Nov 16 2022 ··· 20 15 Contact: "Jithu Joseph" <jithu.joseph@intel.com> 21 16 Description: The status of the last test. It can be one of "pass", "fail" 22 17 or "untested". 18 + Devices: all 23 19 24 20 What: /sys/devices/virtual/misc/intel_ifs_<N>/details 25 21 Date: Nov 16 2022 26 22 KernelVersion: 6.2 27 23 Contact: "Jithu Joseph" <jithu.joseph@intel.com> 28 24 Description: Additional information regarding the last test. The details file reports 29 - the hex value of the SCAN_STATUS MSR. Note that the error_code field 25 + the hex value of the STATUS MSR for this test. Note that the error_code field 30 26 may contain driver defined software code not defined in the Intel SDM. 27 + Devices: all 31 28 32 29 What: /sys/devices/virtual/misc/intel_ifs_<N>/image_version 33 30 Date: Nov 16 2022 34 31 KernelVersion: 6.2 35 32 Contact: "Jithu Joseph" <jithu.joseph@intel.com> 36 - Description: Version (hexadecimal) of loaded IFS binary image. If no scan image 37 - is loaded reports "none". 33 + Description: Version (hexadecimal) of loaded IFS test image. If no test image 34 + is loaded reports "none". Only present for device instances where a test image 35 + is applicable. 36 + Devices: intel_ifs_0 38 37 39 38 What: /sys/devices/virtual/misc/intel_ifs_<N>/current_batch 40 39 Date: Nov 16 2022 ··· 48 39 The number written treated as the 2 digit suffix in the following file name: 49 40 /lib/firmware/intel/ifs_<N>/ff-mm-ss-02x.scan 50 41 Reading the file will provide the suffix of the currently loaded IFS test image. 42 + This file is present only for device instances where a test image is applicable. 43 + Devices: intel_ifs_0
+7
Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
··· 68 68 Wasted burnt and invalid 69 69 Invalid not burnt but marked as valid (error state). 70 70 ======= =============================================== 71 + 72 + What: /sys/bus/platform/devices/MLNXBF04:00/bootfifo 73 + Date: Apr 2023 74 + KernelVersion: 6.4 75 + Contact: "Liming Sun <limings@nvidia.com>" 76 + Description: 77 + The file used to access the BlueField boot fifo.
+8 -7
MAINTAINERS
··· 14148 14148 F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt 14149 14149 F: drivers/net/ieee802154/mrf24j40.c 14150 14150 14151 + MSI EC DRIVER 14152 + M: Nikita Kravets <teackot@gmail.com> 14153 + L: platform-driver-x86@vger.kernel.org 14154 + S: Maintained 14155 + W: https://github.com/BeardOverflow/msi-ec 14156 + F: drivers/platform/x86/msi-ec.* 14157 + 14151 14158 MSI LAPTOP SUPPORT 14152 14159 M: "Lee, Chun-Yi" <jlee@suse.com> 14153 14160 L: platform-driver-x86@vger.kernel.org ··· 16336 16329 S: Maintained 16337 16330 F: crypto/pcrypt.c 16338 16331 F: include/crypto/pcrypt.h 16339 - 16340 - PEAQ WMI HOTKEYS DRIVER 16341 - M: Hans de Goede <hdegoede@redhat.com> 16342 - L: platform-driver-x86@vger.kernel.org 16343 - S: Maintained 16344 - F: drivers/platform/x86/peaq-wmi.c 16345 16332 16346 16333 PECI HARDWARE MONITORING DRIVERS 16347 16334 M: Iwona Winiarska <iwona.winiarska@intel.com> ··· 22726 22725 L: platform-driver-x86@vger.kernel.org 22727 22726 S: Maintained 22728 22727 T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git 22729 - F: drivers/platform/x86/x86-android-tablets.c 22728 + F: drivers/platform/x86/x86-android-tablets/ 22730 22729 22731 22730 X86 PLATFORM DRIVERS 22732 22731 M: Hans de Goede <hdegoede@redhat.com>
+2
arch/x86/include/asm/msr-index.h
··· 206 206 207 207 /* Abbreviated from Intel SDM name IA32_INTEGRITY_CAPABILITIES */ 208 208 #define MSR_INTEGRITY_CAPS 0x000002d9 209 + #define MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT 2 210 + #define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT) 209 211 #define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4 210 212 #define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT) 211 213
+87
drivers/platform/mellanox/mlxbf-bootctl.c
··· 10 10 11 11 #include <linux/acpi.h> 12 12 #include <linux/arm-smccc.h> 13 + #include <linux/delay.h> 13 14 #include <linux/module.h> 14 15 #include <linux/platform_device.h> 15 16 ··· 44 43 [2] = "GA Non-Secured", 45 44 [3] = "RMA", 46 45 }; 46 + 47 + /* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */ 48 + static void __iomem *mlxbf_rsh_boot_data; 49 + static void __iomem *mlxbf_rsh_boot_cnt; 47 50 48 51 /* ARM SMC call which is atomic and no need for lock. */ 49 52 static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) ··· 249 244 return buf_len; 250 245 } 251 246 247 + static ssize_t fw_reset_store(struct device *dev, 248 + struct device_attribute *attr, 249 + const char *buf, size_t count) 250 + { 251 + unsigned long key; 252 + int err; 253 + 254 + err = kstrtoul(buf, 16, &key); 255 + if (err) 256 + return err; 257 + 258 + if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, key) < 0) 259 + return -EINVAL; 260 + 261 + return count; 262 + } 263 + 252 264 static DEVICE_ATTR_RW(post_reset_wdog); 253 265 static DEVICE_ATTR_RW(reset_action); 254 266 static DEVICE_ATTR_RW(second_reset_action); 255 267 static DEVICE_ATTR_RO(lifecycle_state); 256 268 static DEVICE_ATTR_RO(secure_boot_fuse_state); 269 + static DEVICE_ATTR_WO(fw_reset); 257 270 258 271 static struct attribute *mlxbf_bootctl_attrs[] = { 259 272 &dev_attr_post_reset_wdog.attr, ··· 279 256 &dev_attr_second_reset_action.attr, 280 257 &dev_attr_lifecycle_state.attr, 281 258 &dev_attr_secure_boot_fuse_state.attr, 259 + &dev_attr_fw_reset.attr, 282 260 NULL 283 261 }; 284 262 ··· 291 267 }; 292 268 293 269 MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); 270 + 271 + static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, 272 + struct kobject *kobj, 273 + struct bin_attribute *bin_attr, 274 + char *buf, loff_t pos, 275 + size_t count) 276 + { 277 + unsigned long timeout = msecs_to_jiffies(500); 278 + unsigned long expire = jiffies + timeout; 279 + u64 data, cnt = 0; 280 + char *p = buf; 281 + 282 + while (count >= sizeof(data)) { 283 + /* Give up reading if no more data within 500ms. */ 284 + if (!cnt) { 285 + cnt = readq(mlxbf_rsh_boot_cnt); 286 + if (!cnt) { 287 + if (time_after(jiffies, expire)) 288 + break; 289 + usleep_range(10, 50); 290 + continue; 291 + } 292 + } 293 + 294 + data = readq(mlxbf_rsh_boot_data); 295 + memcpy(p, &data, sizeof(data)); 296 + count -= sizeof(data); 297 + p += sizeof(data); 298 + cnt--; 299 + expire = jiffies + timeout; 300 + } 301 + 302 + return p - buf; 303 + } 304 + 305 + static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { 306 + .attr = { .name = "bootfifo", .mode = 0400 }, 307 + .read = mlxbf_bootctl_bootfifo_read, 308 + }; 294 309 295 310 static bool mlxbf_bootctl_guid_match(const guid_t *guid, 296 311 const struct arm_smccc_res *res) ··· 348 285 guid_t guid; 349 286 int ret; 350 287 288 + /* Get the resource of the bootfifo data register. */ 289 + mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0); 290 + if (IS_ERR(mlxbf_rsh_boot_data)) 291 + return PTR_ERR(mlxbf_rsh_boot_data); 292 + 293 + /* Get the resource of the bootfifo counter register. */ 294 + mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1); 295 + if (IS_ERR(mlxbf_rsh_boot_cnt)) 296 + return PTR_ERR(mlxbf_rsh_boot_cnt); 297 + 351 298 /* Ensure we have the UUID we expect for this service. */ 352 299 arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); 353 300 guid_parse(mlxbf_bootctl_svc_uuid_str, &guid); ··· 375 302 if (ret < 0) 376 303 dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n"); 377 304 305 + ret = sysfs_create_bin_file(&pdev->dev.kobj, 306 + &mlxbf_bootctl_bootfifo_sysfs_attr); 307 + if (ret) 308 + pr_err("Unable to create bootfifo sysfs file, error %d\n", ret); 309 + 310 + return ret; 311 + } 312 + 313 + static int mlxbf_bootctl_remove(struct platform_device *pdev) 314 + { 315 + sysfs_remove_bin_file(&pdev->dev.kobj, 316 + &mlxbf_bootctl_bootfifo_sysfs_attr); 317 + 378 318 return 0; 379 319 } 380 320 381 321 static struct platform_driver mlxbf_bootctl_driver = { 382 322 .probe = mlxbf_bootctl_probe, 323 + .remove = mlxbf_bootctl_remove, 383 324 .driver = { 384 325 .name = "mlxbf-bootctl", 385 326 .dev_groups = mlxbf_bootctl_groups,
+6
drivers/platform/mellanox/mlxbf-bootctl.h
··· 75 75 76 76 #define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008 77 77 78 + /* 79 + * Initiate Firmware Reset via TYU. This might be invoked during the reset 80 + * flow in isolation mode. 81 + */ 82 + #define MLXBF_BOOTCTL_FW_RESET 0x8200000D 83 + 78 84 /* SMC function IDs for SiP Service queries */ 79 85 #define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00 80 86 #define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01
+1
drivers/platform/olpc/olpc-xo175-ec.c
··· 746 746 .of_match_table = olpc_xo175_ec_of_match, 747 747 .pm = &olpc_xo175_ec_pm_ops, 748 748 }, 749 + .id_table = olpc_xo175_ec_id_table, 749 750 .probe = olpc_xo175_ec_probe, 750 751 .remove = olpc_xo175_ec_remove, 751 752 };
+1 -1
drivers/platform/surface/surface_aggregator_registry.c
··· 305 305 &ssam_node_bat_ac, 306 306 &ssam_node_bat_main, 307 307 &ssam_node_tmp_pprof, 308 - /* TODO: Tablet mode switch (via POS subsystem) */ 308 + &ssam_node_pos_tablet_switch, 309 309 &ssam_node_hid_kip_keyboard, 310 310 &ssam_node_hid_kip_penstash, 311 311 &ssam_node_hid_kip_touchpad,
+143 -41
drivers/platform/surface/surface_aggregator_tabletsw.c
··· 20 20 21 21 struct ssam_tablet_sw; 22 22 23 + struct ssam_tablet_sw_state { 24 + u32 source; 25 + u32 state; 26 + }; 27 + 23 28 struct ssam_tablet_sw_ops { 24 - int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); 25 - const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); 26 - bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); 29 + int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state); 30 + const char *(*state_name)(struct ssam_tablet_sw *sw, 31 + const struct ssam_tablet_sw_state *state); 32 + bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, 33 + const struct ssam_tablet_sw_state *state); 27 34 }; 28 35 29 36 struct ssam_tablet_sw { 30 37 struct ssam_device *sdev; 31 38 32 - u32 state; 39 + struct ssam_tablet_sw_state state; 33 40 struct work_struct update_work; 34 41 struct input_dev *mode_switch; 35 42 ··· 52 45 53 46 struct { 54 47 u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event); 55 - int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); 56 - const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); 57 - bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); 48 + int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state); 49 + const char *(*state_name)(struct ssam_tablet_sw *sw, 50 + const struct ssam_tablet_sw_state *state); 51 + bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, 52 + const struct ssam_tablet_sw_state *state); 58 53 } ops; 59 54 60 55 struct { ··· 70 61 static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) 71 62 { 72 63 struct ssam_tablet_sw *sw = dev_get_drvdata(dev); 73 - const char *state = sw->ops.state_name(sw, sw->state); 64 + const char *state = sw->ops.state_name(sw, &sw->state); 74 65 75 66 return sysfs_emit(buf, "%s\n", state); 76 67 } ··· 88 79 static void ssam_tablet_sw_update_workfn(struct work_struct *work) 89 80 { 90 81 struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work); 82 + struct ssam_tablet_sw_state state; 91 83 int tablet, status; 92 - u32 state; 93 84 94 85 status = sw->ops.get_state(sw, &state); 95 86 if (status) 96 87 return; 97 88 98 - if (sw->state == state) 89 + if (sw->state.source == state.source && sw->state.state == state.state) 99 90 return; 100 91 sw->state = state; 101 92 102 93 /* Send SW_TABLET_MODE event. */ 103 - tablet = sw->ops.state_is_tablet_mode(sw, state); 94 + tablet = sw->ops.state_is_tablet_mode(sw, &state); 104 95 input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); 105 96 input_sync(sw->mode_switch); 106 97 } ··· 155 146 sw->mode_switch->id.bustype = BUS_HOST; 156 147 sw->mode_switch->dev.parent = &sdev->dev; 157 148 158 - tablet = sw->ops.state_is_tablet_mode(sw, sw->state); 149 + tablet = sw->ops.state_is_tablet_mode(sw, &sw->state); 159 150 input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE); 160 151 input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); 161 152 ··· 212 203 SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05, 213 204 }; 214 205 215 - static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state) 206 + static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, 207 + const struct ssam_tablet_sw_state *state) 216 208 { 217 - switch (state) { 209 + switch (state->state) { 218 210 case SSAM_KIP_COVER_STATE_DISCONNECTED: 219 211 return "disconnected"; 220 212 ··· 232 222 return "folded-back"; 233 223 234 224 default: 235 - dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state); 225 + dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state->state); 236 226 return "<unknown>"; 237 227 } 238 228 } 239 229 240 - static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) 230 + static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, 231 + const struct ssam_tablet_sw_state *state) 241 232 { 242 - switch (state) { 233 + switch (state->state) { 243 234 case SSAM_KIP_COVER_STATE_DISCONNECTED: 244 235 case SSAM_KIP_COVER_STATE_FOLDED_CANVAS: 245 236 case SSAM_KIP_COVER_STATE_FOLDED_BACK: ··· 251 240 return false; 252 241 253 242 default: 254 - dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", sw->state); 243 + dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", state->state); 255 244 return true; 256 245 } 257 246 } ··· 263 252 .instance_id = 0x00, 264 253 }); 265 254 266 - static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state) 255 + static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state) 267 256 { 268 257 int status; 269 258 u8 raw; ··· 274 263 return status; 275 264 } 276 265 277 - *state = raw; 266 + state->source = 0; /* Unused for KIP switch. */ 267 + state->state = raw; 278 268 return 0; 279 269 } 280 270 ··· 324 312 #define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03 325 313 #define SSAM_POS_MAX_SOURCES 4 326 314 327 - enum ssam_pos_state { 328 - SSAM_POS_POSTURE_LID_CLOSED = 0x00, 329 - SSAM_POS_POSTURE_LAPTOP = 0x01, 330 - SSAM_POS_POSTURE_SLATE = 0x02, 331 - SSAM_POS_POSTURE_TABLET = 0x03, 315 + enum ssam_pos_source_id { 316 + SSAM_POS_SOURCE_COVER = 0x00, 317 + SSAM_POS_SOURCE_SLS = 0x03, 318 + }; 319 + 320 + enum ssam_pos_state_cover { 321 + SSAM_POS_COVER_DISCONNECTED = 0x01, 322 + SSAM_POS_COVER_CLOSED = 0x02, 323 + SSAM_POS_COVER_LAPTOP = 0x03, 324 + SSAM_POS_COVER_FOLDED_CANVAS = 0x04, 325 + SSAM_POS_COVER_FOLDED_BACK = 0x05, 326 + }; 327 + 328 + enum ssam_pos_state_sls { 329 + SSAM_POS_SLS_LID_CLOSED = 0x00, 330 + SSAM_POS_SLS_LAPTOP = 0x01, 331 + SSAM_POS_SLS_SLATE = 0x02, 332 + SSAM_POS_SLS_TABLET = 0x03, 332 333 }; 333 334 334 335 struct ssam_sources_list { ··· 349 324 __le32 id[SSAM_POS_MAX_SOURCES]; 350 325 } __packed; 351 326 352 - static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, u32 state) 327 + static const char *ssam_pos_state_name_cover(struct ssam_tablet_sw *sw, u32 state) 353 328 { 354 329 switch (state) { 355 - case SSAM_POS_POSTURE_LID_CLOSED: 330 + case SSAM_POS_COVER_DISCONNECTED: 331 + return "disconnected"; 332 + 333 + case SSAM_POS_COVER_CLOSED: 356 334 return "closed"; 357 335 358 - case SSAM_POS_POSTURE_LAPTOP: 336 + case SSAM_POS_COVER_LAPTOP: 359 337 return "laptop"; 360 338 361 - case SSAM_POS_POSTURE_SLATE: 362 - return "slate"; 339 + case SSAM_POS_COVER_FOLDED_CANVAS: 340 + return "folded-canvas"; 363 341 364 - case SSAM_POS_POSTURE_TABLET: 365 - return "tablet"; 342 + case SSAM_POS_COVER_FOLDED_BACK: 343 + return "folded-back"; 366 344 367 345 default: 368 - dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); 346 + dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state); 369 347 return "<unknown>"; 370 348 } 371 349 } 372 350 373 - static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) 351 + static const char *ssam_pos_state_name_sls(struct ssam_tablet_sw *sw, u32 state) 374 352 { 375 353 switch (state) { 376 - case SSAM_POS_POSTURE_LAPTOP: 377 - case SSAM_POS_POSTURE_LID_CLOSED: 354 + case SSAM_POS_SLS_LID_CLOSED: 355 + return "closed"; 356 + 357 + case SSAM_POS_SLS_LAPTOP: 358 + return "laptop"; 359 + 360 + case SSAM_POS_SLS_SLATE: 361 + return "slate"; 362 + 363 + case SSAM_POS_SLS_TABLET: 364 + return "tablet"; 365 + 366 + default: 367 + dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state); 368 + return "<unknown>"; 369 + } 370 + } 371 + 372 + static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, 373 + const struct ssam_tablet_sw_state *state) 374 + { 375 + switch (state->source) { 376 + case SSAM_POS_SOURCE_COVER: 377 + return ssam_pos_state_name_cover(sw, state->state); 378 + 379 + case SSAM_POS_SOURCE_SLS: 380 + return ssam_pos_state_name_sls(sw, state->state); 381 + 382 + default: 383 + dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source); 384 + return "<unknown>"; 385 + } 386 + } 387 + 388 + static bool ssam_pos_state_is_tablet_mode_cover(struct ssam_tablet_sw *sw, u32 state) 389 + { 390 + switch (state) { 391 + case SSAM_POS_COVER_DISCONNECTED: 392 + case SSAM_POS_COVER_FOLDED_CANVAS: 393 + case SSAM_POS_COVER_FOLDED_BACK: 394 + return true; 395 + 396 + case SSAM_POS_COVER_CLOSED: 397 + case SSAM_POS_COVER_LAPTOP: 378 398 return false; 379 399 380 - case SSAM_POS_POSTURE_SLATE: 400 + default: 401 + dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state); 402 + return true; 403 + } 404 + } 405 + 406 + static bool ssam_pos_state_is_tablet_mode_sls(struct ssam_tablet_sw *sw, u32 state) 407 + { 408 + switch (state) { 409 + case SSAM_POS_SLS_LAPTOP: 410 + case SSAM_POS_SLS_LID_CLOSED: 411 + return false; 412 + 413 + case SSAM_POS_SLS_SLATE: 381 414 return tablet_mode_in_slate_state; 382 415 383 - case SSAM_POS_POSTURE_TABLET: 416 + case SSAM_POS_SLS_TABLET: 384 417 return true; 385 418 386 419 default: 387 - dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); 420 + dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state); 421 + return true; 422 + } 423 + } 424 + 425 + static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, 426 + const struct ssam_tablet_sw_state *state) 427 + { 428 + switch (state->source) { 429 + case SSAM_POS_SOURCE_COVER: 430 + return ssam_pos_state_is_tablet_mode_cover(sw, state->state); 431 + 432 + case SSAM_POS_SOURCE_SLS: 433 + return ssam_pos_state_is_tablet_mode_sls(sw, state->state); 434 + 435 + default: 436 + dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source); 388 437 return true; 389 438 } 390 439 } ··· 549 450 return 0; 550 451 } 551 452 552 - static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state) 453 + static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state) 553 454 { 554 455 u32 source_id; 456 + u32 source_state; 555 457 int status; 556 458 557 459 status = ssam_pos_get_source(sw, &source_id); ··· 561 461 return status; 562 462 } 563 463 564 - status = ssam_pos_get_posture_for_source(sw, source_id, state); 464 + status = ssam_pos_get_posture_for_source(sw, source_id, &source_state); 565 465 if (status) { 566 466 dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n", 567 467 source_id, status); 568 468 return status; 569 469 } 570 470 471 + state->source = source_id; 472 + state->state = source_state; 571 473 return 0; 572 474 } 573 475
+18 -24
drivers/platform/x86/Kconfig
··· 84 84 MXM is a standard for laptop graphics cards, the WMI interface 85 85 is required for switchable nvidia graphics machines 86 86 87 - config PEAQ_WMI 88 - tristate "PEAQ 2-in-1 WMI hotkey driver" 89 - depends on ACPI_WMI 90 - depends on INPUT 91 - help 92 - Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. 93 - 94 87 config NVIDIA_WMI_EC_BACKLIGHT 95 88 tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems" 96 89 depends on ACPI_VIDEO ··· 206 213 depends on ACPI && PCI 207 214 depends on PNP 208 215 depends on BACKLIGHT_CLASS_DEVICE 209 - depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE 210 216 help 211 217 This driver provides support for the gmux device found on many 212 218 Apple laptops, which controls the display mux for the hybrid ··· 461 469 This is a driver for Lenovo IdeaPad netbooks contains drivers for 462 470 rfkill switch, hotkey, fan control and backlight control. 463 471 472 + config LENOVO_YMC 473 + tristate "Lenovo Yoga Tablet Mode Control" 474 + depends on ACPI_WMI 475 + depends on INPUT 476 + select INPUT_SPARSEKMAP 477 + help 478 + This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input 479 + events for Lenovo Yoga notebooks. 480 + 464 481 config SENSORS_HDAPS 465 482 tristate "Thinkpad Hard Drive Active Protection System (hdaps)" 466 483 depends on INPUT ··· 643 642 be called think-lmi. 644 643 645 644 source "drivers/platform/x86/intel/Kconfig" 645 + 646 + config MSI_EC 647 + tristate "MSI EC Extras" 648 + depends on ACPI 649 + depends on ACPI_BATTERY 650 + help 651 + This driver allows various MSI laptops' functionalities to be 652 + controlled from userspace, including battery charge threshold. 646 653 647 654 config MSI_LAPTOP 648 655 tristate "MSI Laptop Extras" ··· 987 978 the OS-image for the device. This option supplies the missing info. 988 979 Enable this for x86 tablets with Silead or Chipone touchscreens. 989 980 990 - config X86_ANDROID_TABLETS 991 - tristate "X86 Android tablet support" 992 - depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB 993 - help 994 - X86 tablets which ship with Android as (part of) the factory image 995 - typically have various problems with their DSDTs. The factory kernels 996 - shipped on these devices typically have device addresses and GPIOs 997 - hardcoded in the kernel, rather than specified in their DSDT. 998 - 999 - With the DSDT containing a random collection of devices which may or 1000 - may not actually be present. This driver contains various fixes for 1001 - such tablets, including instantiating kernel devices for devices which 1002 - are missing from the DSDT. 1003 - 1004 - If you have a x86 Android tablet say Y or M here, for a generic x86 1005 - distro config say M here. 981 + source "drivers/platform/x86/x86-android-tablets/Kconfig" 1006 982 1007 983 config FW_ATTR_CLASS 1008 984 tristate
+3 -2
drivers/platform/x86/Makefile
··· 12 12 obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o 13 13 obj-$(CONFIG_MXM_WMI) += mxm-wmi.o 14 14 obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o 15 - obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o 16 15 obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o 17 16 obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o 18 17 obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o ··· 62 63 # IBM Thinkpad and Lenovo 63 64 obj-$(CONFIG_IBM_RTL) += ibm_rtl.o 64 65 obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o 66 + obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o 65 67 obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o 66 68 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 67 69 obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o ··· 71 71 obj-y += intel/ 72 72 73 73 # MSI 74 + obj-$(CONFIG_MSI_EC) += msi-ec.o 74 75 obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o 75 76 obj-$(CONFIG_MSI_WMI) += msi-wmi.o 76 77 ··· 113 112 obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o 114 113 obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o 115 114 obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o 116 - obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o 115 + obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets/ 117 116 118 117 # Intel uncore drivers 119 118 obj-$(CONFIG_INTEL_IPS) += intel_ips.o
+2 -3
drivers/platform/x86/acer-wmi.c
··· 2258 2258 return err; 2259 2259 } 2260 2260 2261 - static int acer_platform_remove(struct platform_device *device) 2261 + static void acer_platform_remove(struct platform_device *device) 2262 2262 { 2263 2263 if (has_cap(ACER_CAP_MAILLED)) 2264 2264 acer_led_exit(); ··· 2266 2266 acer_backlight_exit(); 2267 2267 2268 2268 acer_rfkill_exit(); 2269 - return 0; 2270 2269 } 2271 2270 2272 2271 #ifdef CONFIG_PM_SLEEP ··· 2333 2334 .pm = &acer_pm, 2334 2335 }, 2335 2336 .probe = acer_platform_probe, 2336 - .remove = acer_platform_remove, 2337 + .remove_new = acer_platform_remove, 2337 2338 .shutdown = acer_platform_shutdown, 2338 2339 }; 2339 2340
+1 -1
drivers/platform/x86/acerhdf.c
··· 341 341 pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n", 342 342 fanoff, fanon, fanon); 343 343 fanoff = fanon; 344 - }; 344 + } 345 345 346 346 trips[0].temperature = fanon; 347 347 trips[0].hysteresis = fanon - fanoff;
+2 -4
drivers/platform/x86/adv_swbutton.c
··· 90 90 return 0; 91 91 } 92 92 93 - static int adv_swbutton_remove(struct platform_device *device) 93 + static void adv_swbutton_remove(struct platform_device *device) 94 94 { 95 95 acpi_handle handle = ACPI_HANDLE(&device->dev); 96 96 97 97 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, 98 98 adv_swbutton_notify); 99 - 100 - return 0; 101 99 } 102 100 103 101 static const struct acpi_device_id button_device_ids[] = { ··· 110 112 .acpi_match_table = button_device_ids, 111 113 }, 112 114 .probe = adv_swbutton_probe, 113 - .remove = adv_swbutton_remove, 115 + .remove_new = adv_swbutton_remove, 114 116 }; 115 117 module_platform_driver(adv_swbutton_driver); 116 118
+1 -1
drivers/platform/x86/amd/Kconfig
··· 7 7 8 8 config AMD_PMC 9 9 tristate "AMD SoC PMC driver" 10 - depends on ACPI && PCI && RTC_CLASS 10 + depends on ACPI && PCI && RTC_CLASS && AMD_NB 11 11 select SERIO 12 12 help 13 13 The driver provides support for AMD Power Management Controller
+2 -4
drivers/platform/x86/amd/hsmp.c
··· 340 340 return misc_register(&hsmp_device); 341 341 } 342 342 343 - static int hsmp_pltdrv_remove(struct platform_device *pdev) 343 + static void hsmp_pltdrv_remove(struct platform_device *pdev) 344 344 { 345 345 misc_deregister(&hsmp_device); 346 - 347 - return 0; 348 346 } 349 347 350 348 static struct platform_driver amd_hsmp_driver = { 351 349 .probe = hsmp_pltdrv_probe, 352 - .remove = hsmp_pltdrv_remove, 350 + .remove_new = hsmp_pltdrv_remove, 353 351 .driver = { 354 352 .name = DRIVER_NAME, 355 353 },
+85 -85
drivers/platform/x86/amd/pmc.c
··· 10 10 11 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 12 13 + #include <asm/amd_nb.h> 13 14 #include <linux/acpi.h> 14 15 #include <linux/bitfield.h> 15 16 #include <linux/bits.h> ··· 38 37 #define AMD_PMC_SCRATCH_REG_YC 0xD14 39 38 40 39 /* STB Registers */ 41 - #define AMD_PMC_STB_INDEX_ADDRESS 0xF8 42 - #define AMD_PMC_STB_INDEX_DATA 0xFC 43 40 #define AMD_PMC_STB_PMI_0 0x03E30600 44 41 #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 45 42 #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 ··· 55 56 #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 56 57 57 58 /* Base address of SMU for mapping physical address to virtual address */ 58 - #define AMD_PMC_SMU_INDEX_ADDRESS 0xB8 59 - #define AMD_PMC_SMU_INDEX_DATA 0xBC 60 59 #define AMD_PMC_MAPPING_SIZE 0x01000 61 60 #define AMD_PMC_BASE_ADDR_OFFSET 0x10000 62 61 #define AMD_PMC_BASE_ADDR_LO 0x13B102E8 ··· 94 97 #define AMD_CPU_ID_YC 0x14B5 95 98 #define AMD_CPU_ID_CB 0x14D8 96 99 #define AMD_CPU_ID_PS 0x14E8 100 + #define AMD_CPU_ID_SP 0x14A4 97 101 98 102 #define PMC_MSG_DELAY_MIN_US 50 99 103 #define RESPONSE_REGISTER_LOOP_MAX 20000 ··· 266 268 dev->msg_port = 0; 267 269 if (ret) { 268 270 dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); 271 + kfree(buf); 269 272 return ret; 270 273 } 271 274 ··· 341 342 return 0; 342 343 } 343 344 344 - static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, 345 - struct seq_file *s) 346 - { 347 - u32 val; 348 - 349 - switch (pdev->cpu_id) { 350 - case AMD_CPU_ID_CZN: 351 - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); 352 - break; 353 - case AMD_CPU_ID_YC: 354 - case AMD_CPU_ID_CB: 355 - case AMD_CPU_ID_PS: 356 - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); 357 - break; 358 - default: 359 - return -EINVAL; 360 - } 361 - 362 - if (dev) 363 - dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); 364 - 365 - if (s) 366 - seq_printf(s, "SMU idlemask : 0x%x\n", val); 367 - 368 - return 0; 369 - } 370 - 371 345 static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table) 372 346 { 373 347 if (!pdev->smu_virt_addr) { ··· 374 402 { 375 403 int rc; 376 404 u32 val; 405 + 406 + if (dev->cpu_id == AMD_CPU_ID_PCO) 407 + return -ENODEV; 377 408 378 409 rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); 379 410 if (rc) ··· 424 449 static DEVICE_ATTR_RO(smu_fw_version); 425 450 static DEVICE_ATTR_RO(smu_program); 426 451 452 + static umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) 453 + { 454 + struct device *dev = kobj_to_dev(kobj); 455 + struct amd_pmc_dev *pdev = dev_get_drvdata(dev); 456 + 457 + if (pdev->cpu_id == AMD_CPU_ID_PCO) 458 + return 0; 459 + return 0444; 460 + } 461 + 427 462 static struct attribute *pmc_attrs[] = { 428 463 &dev_attr_smu_fw_version.attr, 429 464 &dev_attr_smu_program.attr, 430 465 NULL, 431 466 }; 432 - ATTRIBUTE_GROUPS(pmc); 467 + 468 + static struct attribute_group pmc_attr_group = { 469 + .attrs = pmc_attrs, 470 + .is_visible = pmc_attr_is_visible, 471 + }; 472 + 473 + static const struct attribute_group *pmc_groups[] = { 474 + &pmc_attr_group, 475 + NULL, 476 + }; 433 477 434 478 static int smu_fw_info_show(struct seq_file *s, void *unused) 435 479 { ··· 515 521 } 516 522 DEFINE_SHOW_ATTRIBUTE(s0ix_stats); 517 523 518 - static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) 524 + static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, 525 + struct seq_file *s) 519 526 { 520 - struct amd_pmc_dev *dev = s->private; 527 + u32 val; 521 528 int rc; 522 529 523 - /* we haven't yet read SMU version */ 524 - if (!dev->major) { 525 - rc = amd_pmc_get_smu_version(dev); 526 - if (rc) 527 - return rc; 530 + switch (pdev->cpu_id) { 531 + case AMD_CPU_ID_CZN: 532 + /* we haven't yet read SMU version */ 533 + if (!pdev->major) { 534 + rc = amd_pmc_get_smu_version(pdev); 535 + if (rc) 536 + return rc; 537 + } 538 + if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37)) 539 + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); 540 + else 541 + return -EINVAL; 542 + break; 543 + case AMD_CPU_ID_YC: 544 + case AMD_CPU_ID_CB: 545 + case AMD_CPU_ID_PS: 546 + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); 547 + break; 548 + default: 549 + return -EINVAL; 528 550 } 529 551 530 - if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) { 531 - rc = amd_pmc_idlemask_read(dev, NULL, s); 532 - if (rc) 533 - return rc; 534 - } else { 535 - seq_puts(s, "Unsupported SMU version for Idlemask\n"); 536 - } 552 + if (dev) 553 + dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); 554 + 555 + if (s) 556 + seq_printf(s, "SMU idlemask : 0x%x\n", val); 537 557 538 558 return 0; 559 + } 560 + 561 + static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) 562 + { 563 + return amd_pmc_idlemask_read(s->private, NULL, s); 539 564 } 540 565 DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask); 541 566 ··· 825 812 dev_err(pdev->dev, "error writing to STB: %d\n", rc); 826 813 } 827 814 815 + static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) 816 + { 817 + if (pdev->cpu_id == AMD_CPU_ID_PCO) 818 + return -ENODEV; 819 + 820 + return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); 821 + } 822 + 828 823 static void amd_pmc_s2idle_restore(void) 829 824 { 830 825 struct amd_pmc_dev *pdev = &pmc; ··· 845 824 dev_err(pdev->dev, "resume failed: %d\n", rc); 846 825 847 826 /* Let SMU know that we are looking for stats */ 848 - amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); 827 + amd_pmc_dump_data(pdev); 849 828 850 829 rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE); 851 830 if (rc) ··· 861 840 .restore = amd_pmc_s2idle_restore, 862 841 }; 863 842 864 - static int __maybe_unused amd_pmc_suspend_handler(struct device *dev) 843 + static int amd_pmc_suspend_handler(struct device *dev) 865 844 { 866 845 struct amd_pmc_dev *pdev = dev_get_drvdata(dev); 867 846 ··· 887 866 { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, 888 867 { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, 889 868 { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, 869 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, 890 870 { } 891 871 }; 892 872 ··· 924 902 { 925 903 int err; 926 904 927 - err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); 905 + err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data); 928 906 if (err) { 929 - dev_err(dev->dev, "failed to write addr in stb: 0x%X\n", 930 - AMD_PMC_STB_INDEX_ADDRESS); 931 - return pcibios_err_to_errno(err); 932 - } 933 - 934 - err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, data); 935 - if (err) { 936 - dev_err(dev->dev, "failed to write data in stb: 0x%X\n", 937 - AMD_PMC_STB_INDEX_DATA); 907 + dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0); 938 908 return pcibios_err_to_errno(err); 939 909 } 940 910 ··· 937 923 { 938 924 int i, err; 939 925 940 - err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); 941 - if (err) { 942 - dev_err(dev->dev, "error writing addr to stb: 0x%X\n", 943 - AMD_PMC_STB_INDEX_ADDRESS); 944 - return pcibios_err_to_errno(err); 945 - } 946 - 947 926 for (i = 0; i < FIFO_SIZE; i++) { 948 - err = pci_read_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, buf++); 927 + err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++); 949 928 if (err) { 950 - dev_err(dev->dev, "error reading data from stb: 0x%X\n", 951 - AMD_PMC_STB_INDEX_DATA); 929 + dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0); 952 930 return pcibios_err_to_errno(err); 953 931 } 954 932 } ··· 966 960 } 967 961 968 962 dev->cpu_id = rdev->device; 969 - dev->rdev = rdev; 970 - err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO); 971 - if (err) { 972 - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); 973 - err = pcibios_err_to_errno(err); 963 + 964 + if (dev->cpu_id == AMD_CPU_ID_SP) { 965 + dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); 966 + err = -ENODEV; 974 967 goto err_pci_dev_put; 975 968 } 976 969 977 - err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); 970 + dev->rdev = rdev; 971 + err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val); 978 972 if (err) { 973 + dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO); 979 974 err = pcibios_err_to_errno(err); 980 975 goto err_pci_dev_put; 981 976 } 982 977 983 978 base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; 984 979 985 - err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI); 980 + err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val); 986 981 if (err) { 987 - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); 988 - err = pcibios_err_to_errno(err); 989 - goto err_pci_dev_put; 990 - } 991 - 992 - err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); 993 - if (err) { 982 + dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI); 994 983 err = pcibios_err_to_errno(err); 995 984 goto err_pci_dev_put; 996 985 } ··· 1023 1022 return err; 1024 1023 } 1025 1024 1026 - static int amd_pmc_remove(struct platform_device *pdev) 1025 + static void amd_pmc_remove(struct platform_device *pdev) 1027 1026 { 1028 1027 struct amd_pmc_dev *dev = platform_get_drvdata(pdev); 1029 1028 ··· 1032 1031 amd_pmc_dbgfs_unregister(dev); 1033 1032 pci_dev_put(dev->rdev); 1034 1033 mutex_destroy(&dev->lock); 1035 - return 0; 1036 1034 } 1037 1035 1038 1036 static const struct acpi_device_id amd_pmc_acpi_ids[] = { ··· 1054 1054 .pm = pm_sleep_ptr(&amd_pmc_pm), 1055 1055 }, 1056 1056 .probe = amd_pmc_probe, 1057 - .remove = amd_pmc_remove, 1057 + .remove_new = amd_pmc_remove, 1058 1058 }; 1059 1059 module_platform_driver(amd_pmc_driver); 1060 1060
+1
drivers/platform/x86/amd/pmf/Kconfig
··· 7 7 tristate "AMD Platform Management Framework" 8 8 depends on ACPI && PCI 9 9 depends on POWER_SUPPLY 10 + depends on AMD_NB 10 11 select ACPI_PLATFORM_PROFILE 11 12 help 12 13 This driver provides support for the AMD Platform Management Framework.
+7 -20
drivers/platform/x86/amd/pmf/core.c
··· 8 8 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 9 9 */ 10 10 11 + #include <asm/amd_nb.h> 11 12 #include <linux/debugfs.h> 12 13 #include <linux/iopoll.h> 13 14 #include <linux/module.h> ··· 23 22 #define AMD_PMF_REGISTER_ARGUMENT 0xA58 24 23 25 24 /* Base address of SMU for mapping physical address to virtual address */ 26 - #define AMD_PMF_SMU_INDEX_ADDRESS 0xB8 27 - #define AMD_PMF_SMU_INDEX_DATA 0xBC 28 25 #define AMD_PMF_MAPPING_SIZE 0x01000 29 26 #define AMD_PMF_BASE_ADDR_OFFSET 0x10000 30 27 #define AMD_PMF_BASE_ADDR_LO 0x13B102E8 ··· 347 348 } 348 349 349 350 dev->cpu_id = rdev->device; 350 - err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO); 351 - if (err) { 352 - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS); 353 - pci_dev_put(rdev); 354 - return pcibios_err_to_errno(err); 355 - } 356 351 357 - err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); 352 + err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val); 358 353 if (err) { 354 + dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO); 359 355 pci_dev_put(rdev); 360 356 return pcibios_err_to_errno(err); 361 357 } 362 358 363 359 base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK; 364 360 365 - err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI); 361 + err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val); 366 362 if (err) { 367 - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS); 368 - pci_dev_put(rdev); 369 - return pcibios_err_to_errno(err); 370 - } 371 - 372 - err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); 373 - if (err) { 363 + dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI); 374 364 pci_dev_put(rdev); 375 365 return pcibios_err_to_errno(err); 376 366 } ··· 390 402 return 0; 391 403 } 392 404 393 - static int amd_pmf_remove(struct platform_device *pdev) 405 + static void amd_pmf_remove(struct platform_device *pdev) 394 406 { 395 407 struct amd_pmf_dev *dev = platform_get_drvdata(pdev); 396 408 ··· 401 413 mutex_destroy(&dev->lock); 402 414 mutex_destroy(&dev->update_mutex); 403 415 kfree(dev->buf); 404 - return 0; 405 416 } 406 417 407 418 static const struct attribute_group *amd_pmf_driver_groups[] = { ··· 415 428 .dev_groups = amd_pmf_driver_groups, 416 429 }, 417 430 .probe = amd_pmf_probe, 418 - .remove = amd_pmf_remove, 431 + .remove_new = amd_pmf_remove, 419 432 }; 420 433 module_platform_driver(amd_pmf_driver); 421 434
+2 -3
drivers/platform/x86/amilo-rfkill.c
··· 124 124 return rc; 125 125 } 126 126 127 - static int amilo_rfkill_remove(struct platform_device *device) 127 + static void amilo_rfkill_remove(struct platform_device *device) 128 128 { 129 129 rfkill_unregister(amilo_rfkill_dev); 130 130 rfkill_destroy(amilo_rfkill_dev); 131 - return 0; 132 131 } 133 132 134 133 static struct platform_driver amilo_rfkill_driver = { ··· 135 136 .name = KBUILD_MODNAME, 136 137 }, 137 138 .probe = amilo_rfkill_probe, 138 - .remove = amilo_rfkill_remove, 139 + .remove_new = amilo_rfkill_remove, 139 140 }; 140 141 141 142 static int __init amilo_rfkill_init(void)
+327 -75
drivers/platform/x86/apple-gmux.c
··· 5 5 * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com> 6 6 * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de> 7 7 * Copyright (C) 2015 Lukas Wunner <lukas@wunner.de> 8 + * Copyright (C) 2023 Orlando Chamberlain <orlandoch.dev@gmail.com> 8 9 */ 9 10 10 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ··· 16 15 #include <linux/backlight.h> 17 16 #include <linux/acpi.h> 18 17 #include <linux/pnp.h> 19 - #include <linux/apple_bl.h> 20 18 #include <linux/apple-gmux.h> 21 19 #include <linux/slab.h> 22 20 #include <linux/delay.h> 23 21 #include <linux/pci.h> 24 22 #include <linux/vga_switcheroo.h> 23 + #include <linux/debugfs.h> 24 + #include <acpi/video.h> 25 25 #include <asm/io.h> 26 26 27 27 /** 28 28 * DOC: Overview 29 29 * 30 30 * gmux is a microcontroller built into the MacBook Pro to support dual GPUs: 31 - * A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on retinas. 31 + * A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on pre-T2 retinas. 32 + * 33 + * On T2 Macbooks, the gmux is part of the T2 Coprocessor's SMC. The SMC has 34 + * an I2C connection to a `NXP PCAL6524` GPIO expander, which enables/disables 35 + * the voltage regulators of the discrete GPU, drives the display panel power, 36 + * and has a GPIO to switch the eDP mux. The Intel CPU can interact with 37 + * gmux through MMIO, similar to how the main SMC interface is controlled. 32 38 * 33 39 * (The MacPro6,1 2013 also has a gmux, however it is unclear why since it has 34 40 * dual GPUs but no built-in display.) 35 41 * 36 42 * gmux is connected to the LPC bus of the southbridge. Its I/O ports are 37 43 * accessed differently depending on the microcontroller: Driver functions 38 - * to access a pre-retina gmux are infixed ``_pio_``, those for a retina gmux 39 - * are infixed ``_index_``. 44 + * to access a pre-retina gmux are infixed ``_pio_``, those for a pre-T2 45 + * retina gmux are infixed ``_index_``, and those on T2 Macs are infixed 46 + * with ``_mmio_``. 40 47 * 41 48 * .. _Lattice XP2: 42 49 * http://www.latticesemi.com/en/Products/FPGAandCPLD/LatticeXP2.aspx 43 50 * .. _Renesas R4F2113: 44 51 * http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp 52 + * .. _NXP PCAL6524: 53 + * https://www.nxp.com/docs/en/data-sheet/PCAL6524.pdf 45 54 */ 46 55 56 + struct apple_gmux_config; 57 + 47 58 struct apple_gmux_data { 59 + u8 __iomem *iomem_base; 48 60 unsigned long iostart; 49 61 unsigned long iolen; 50 - bool indexed; 62 + const struct apple_gmux_config *config; 51 63 struct mutex index_lock; 52 64 53 65 struct backlight_device *bdev; ··· 74 60 enum vga_switcheroo_client_id switch_state_external; 75 61 enum vga_switcheroo_state power_state; 76 62 struct completion powerchange_done; 63 + 64 + /* debugfs data */ 65 + u8 selected_port; 66 + struct dentry *debug_dentry; 77 67 }; 78 68 79 69 static struct apple_gmux_data *apple_gmux_data; 70 + 71 + struct apple_gmux_config { 72 + u8 (*read8)(struct apple_gmux_data *gmux_data, int port); 73 + void (*write8)(struct apple_gmux_data *gmux_data, int port, u8 val); 74 + u32 (*read32)(struct apple_gmux_data *gmux_data, int port); 75 + void (*write32)(struct apple_gmux_data *gmux_data, int port, u32 val); 76 + const struct vga_switcheroo_handler *gmux_handler; 77 + enum vga_switcheroo_handler_flags_t handler_flags; 78 + unsigned long resource_type; 79 + bool read_version_as_u32; 80 + char *name; 81 + }; 80 82 81 83 #define GMUX_INTERRUPT_ENABLE 0xff 82 84 #define GMUX_INTERRUPT_DISABLE 0x00 ··· 223 193 mutex_unlock(&gmux_data->index_lock); 224 194 } 225 195 196 + static int gmux_mmio_wait(struct apple_gmux_data *gmux_data) 197 + { 198 + int i = 200; 199 + u8 gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); 200 + 201 + while (i && gwr) { 202 + gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); 203 + udelay(100); 204 + i--; 205 + } 206 + 207 + return !!i; 208 + } 209 + 210 + static u8 gmux_mmio_read8(struct apple_gmux_data *gmux_data, int port) 211 + { 212 + u8 val; 213 + 214 + mutex_lock(&gmux_data->index_lock); 215 + gmux_mmio_wait(gmux_data); 216 + iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); 217 + iowrite8(GMUX_MMIO_READ | sizeof(val), 218 + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); 219 + gmux_mmio_wait(gmux_data); 220 + val = ioread8(gmux_data->iomem_base); 221 + mutex_unlock(&gmux_data->index_lock); 222 + 223 + return val; 224 + } 225 + 226 + static void gmux_mmio_write8(struct apple_gmux_data *gmux_data, int port, 227 + u8 val) 228 + { 229 + mutex_lock(&gmux_data->index_lock); 230 + gmux_mmio_wait(gmux_data); 231 + iowrite8(val, gmux_data->iomem_base); 232 + 233 + iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); 234 + iowrite8(GMUX_MMIO_WRITE | sizeof(val), 235 + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); 236 + 237 + gmux_mmio_wait(gmux_data); 238 + mutex_unlock(&gmux_data->index_lock); 239 + } 240 + 241 + static u32 gmux_mmio_read32(struct apple_gmux_data *gmux_data, int port) 242 + { 243 + u32 val; 244 + 245 + mutex_lock(&gmux_data->index_lock); 246 + gmux_mmio_wait(gmux_data); 247 + iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); 248 + iowrite8(GMUX_MMIO_READ | sizeof(val), 249 + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); 250 + gmux_mmio_wait(gmux_data); 251 + val = be32_to_cpu(ioread32(gmux_data->iomem_base)); 252 + mutex_unlock(&gmux_data->index_lock); 253 + 254 + return val; 255 + } 256 + 257 + static void gmux_mmio_write32(struct apple_gmux_data *gmux_data, int port, 258 + u32 val) 259 + { 260 + mutex_lock(&gmux_data->index_lock); 261 + iowrite32(cpu_to_be32(val), gmux_data->iomem_base); 262 + iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); 263 + iowrite8(GMUX_MMIO_WRITE | sizeof(val), 264 + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); 265 + gmux_mmio_wait(gmux_data); 266 + mutex_unlock(&gmux_data->index_lock); 267 + } 268 + 226 269 static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) 227 270 { 228 - if (gmux_data->indexed) 229 - return gmux_index_read8(gmux_data, port); 230 - else 231 - return gmux_pio_read8(gmux_data, port); 271 + return gmux_data->config->read8(gmux_data, port); 232 272 } 233 273 234 274 static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val) 235 275 { 236 - if (gmux_data->indexed) 237 - gmux_index_write8(gmux_data, port, val); 238 - else 239 - gmux_pio_write8(gmux_data, port, val); 276 + return gmux_data->config->write8(gmux_data, port, val); 240 277 } 241 278 242 279 static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) 243 280 { 244 - if (gmux_data->indexed) 245 - return gmux_index_read32(gmux_data, port); 246 - else 247 - return gmux_pio_read32(gmux_data, port); 281 + return gmux_data->config->read32(gmux_data, port); 248 282 } 249 283 250 284 static void gmux_write32(struct apple_gmux_data *gmux_data, int port, 251 285 u32 val) 252 286 { 253 - if (gmux_data->indexed) 254 - gmux_index_write32(gmux_data, port, val); 255 - else 256 - gmux_pio_write32(gmux_data, port, val); 287 + return gmux_data->config->write32(gmux_data, port, val); 257 288 } 258 289 259 290 /** ··· 324 233 * the GPU. On dual GPU MacBook Pros by contrast, either GPU may be suspended 325 234 * to conserve energy. Hence the PWM signal needs to be generated by a separate 326 235 * backlight driver which is controlled by gmux. The earliest generation 327 - * MBP5 2008/09 uses a `TI LP8543`_ backlight driver. All newer models 328 - * use a `TI LP8545`_. 236 + * MBP5 2008/09 uses a `TI LP8543`_ backlight driver. Newer models 237 + * use a `TI LP8545`_ or a TI LP8548. 329 238 * 330 239 * .. _TI LP8543: https://www.ti.com/lit/ds/symlink/lp8543.pdf 331 240 * .. _TI LP8545: https://www.ti.com/lit/ds/symlink/lp8545.pdf ··· 389 298 * connecting it either to the discrete GPU or the Thunderbolt controller. 390 299 * Oddly enough, while the full port is no longer switchable, AUX and HPD 391 300 * are still switchable by way of an `NXP CBTL03062`_ (on pre-retinas 392 - * MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on retinas) under the 393 - * control of gmux. Since the integrated GPU is missing the main link, 301 + * MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on pre-t2 retinas) under 302 + * the control of gmux. Since the integrated GPU is missing the main link, 394 303 * external displays appear to it as phantoms which fail to link-train. 395 304 * 396 305 * gmux receives the HPD signal of all display connectors and sends an ··· 437 346 else 438 347 gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS; 439 348 440 - if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) 441 - gmux_data->switch_state_display = VGA_SWITCHEROO_IGD; 442 - else 349 + if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) & 1) 443 350 gmux_data->switch_state_display = VGA_SWITCHEROO_DIS; 351 + else 352 + gmux_data->switch_state_display = VGA_SWITCHEROO_IGD; 444 353 445 354 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2) 446 355 gmux_data->switch_state_external = VGA_SWITCHEROO_IGD; ··· 554 463 return VGA_SWITCHEROO_DIS; 555 464 } 556 465 557 - static const struct vga_switcheroo_handler gmux_handler_indexed = { 466 + static const struct vga_switcheroo_handler gmux_handler_no_ddc = { 558 467 .switchto = gmux_switchto, 559 468 .power_state = gmux_set_power_state, 560 469 .get_client_id = gmux_get_client_id, 561 470 }; 562 471 563 - static const struct vga_switcheroo_handler gmux_handler_classic = { 472 + static const struct vga_switcheroo_handler gmux_handler_ddc = { 564 473 .switchto = gmux_switchto, 565 474 .switch_ddc = gmux_switch_ddc, 566 475 .power_state = gmux_set_power_state, 567 476 .get_client_id = gmux_get_client_id, 568 477 }; 569 478 479 + static const struct apple_gmux_config apple_gmux_pio = { 480 + .read8 = &gmux_pio_read8, 481 + .write8 = &gmux_pio_write8, 482 + .read32 = &gmux_pio_read32, 483 + .write32 = &gmux_pio_write32, 484 + .gmux_handler = &gmux_handler_ddc, 485 + .handler_flags = VGA_SWITCHEROO_CAN_SWITCH_DDC, 486 + .resource_type = IORESOURCE_IO, 487 + .read_version_as_u32 = false, 488 + .name = "classic" 489 + }; 490 + 491 + static const struct apple_gmux_config apple_gmux_index = { 492 + .read8 = &gmux_index_read8, 493 + .write8 = &gmux_index_write8, 494 + .read32 = &gmux_index_read32, 495 + .write32 = &gmux_index_write32, 496 + .gmux_handler = &gmux_handler_no_ddc, 497 + .handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG, 498 + .resource_type = IORESOURCE_IO, 499 + .read_version_as_u32 = true, 500 + .name = "indexed" 501 + }; 502 + 503 + static const struct apple_gmux_config apple_gmux_mmio = { 504 + .read8 = &gmux_mmio_read8, 505 + .write8 = &gmux_mmio_write8, 506 + .read32 = &gmux_mmio_read32, 507 + .write32 = &gmux_mmio_write32, 508 + .gmux_handler = &gmux_handler_no_ddc, 509 + .handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG, 510 + .resource_type = IORESOURCE_MEM, 511 + .read_version_as_u32 = true, 512 + .name = "T2" 513 + }; 514 + 515 + 570 516 /** 571 517 * DOC: Interrupt 572 518 * 573 519 * gmux is also connected to a GPIO pin of the southbridge and thereby is able 574 - * to trigger an ACPI GPE. On the MBP5 2008/09 it's GPIO pin 22 of the Nvidia 575 - * MCP79, on all following generations it's GPIO pin 6 of the Intel PCH. 520 + * to trigger an ACPI GPE. ACPI name GMGP holds this GPIO pin's number. On the 521 + * MBP5 2008/09 it's GPIO pin 22 of the Nvidia MCP79, on following generations 522 + * it's GPIO pin 6 of the Intel PCH, on MMIO gmux's it's pin 21. 523 + * 576 524 * The GPE merely signals that an interrupt occurred, the actual type of event 577 525 * is identified by reading a gmux register. 526 + * 527 + * In addition to the GMGP name, gmux's ACPI device also has two methods GMSP 528 + * and GMLV. GMLV likely means "GMUX Level", and reads the value of the GPIO, 529 + * while GMSP likely means "GMUX Set Polarity", and seems to write to the GPIO's 530 + * value. On newer Macbooks (This was introduced with or sometime before the 531 + * MacBookPro14,3), the ACPI GPE method differentiates between the OS type: On 532 + * Darwin, only a notification is signaled, whereas on other OSes, the GPIO's 533 + * value is read and then inverted. 534 + * 535 + * Because Linux masquerades as Darwin, it ends up in the notification-only code 536 + * path. On MMIO gmux's, this seems to lead to us being unable to clear interrupts, 537 + * unless we call GMSP(0). Without this, there is a flood of status=0 interrupts 538 + * that can't be cleared. This issue seems to be unique to MMIO gmux's. 578 539 */ 579 540 580 541 static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) ··· 653 510 /* to clear interrupts write back current status */ 654 511 status = gmux_interrupt_get_status(gmux_data); 655 512 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status); 513 + /* Prevent flood of status=0 interrupts */ 514 + if (gmux_data->config == &apple_gmux_mmio) 515 + acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0); 656 516 } 657 517 658 518 static void gmux_notify_handler(acpi_handle device, u32 value, void *context) ··· 673 527 674 528 if (status & GMUX_INTERRUPT_STATUS_POWER) 675 529 complete(&gmux_data->powerchange_done); 530 + } 531 + 532 + /** 533 + * DOC: Debugfs Interface 534 + * 535 + * gmux ports can be accessed from userspace as a debugfs interface. For example: 536 + * 537 + * # echo 4 > /sys/kernel/debug/apple_gmux/selected_port 538 + * # cat /sys/kernel/debug/apple_gmux/selected_port_data | xxd -p 539 + * 00000005 540 + * 541 + * Reads 4 bytes from port 4 (GMUX_PORT_VERSION_MAJOR). 542 + * 543 + * 1 and 4 byte writes are also allowed. 544 + */ 545 + 546 + static ssize_t gmux_selected_port_data_write(struct file *file, 547 + const char __user *userbuf, size_t count, loff_t *ppos) 548 + { 549 + struct apple_gmux_data *gmux_data = file->private_data; 550 + 551 + if (*ppos) 552 + return -EINVAL; 553 + 554 + if (count == 1) { 555 + u8 data; 556 + 557 + if (copy_from_user(&data, userbuf, 1)) 558 + return -EFAULT; 559 + 560 + gmux_write8(gmux_data, gmux_data->selected_port, data); 561 + } else if (count == 4) { 562 + u32 data; 563 + 564 + if (copy_from_user(&data, userbuf, 4)) 565 + return -EFAULT; 566 + 567 + gmux_write32(gmux_data, gmux_data->selected_port, data); 568 + } else 569 + return -EINVAL; 570 + 571 + return count; 572 + } 573 + 574 + static ssize_t gmux_selected_port_data_read(struct file *file, 575 + char __user *userbuf, size_t count, loff_t *ppos) 576 + { 577 + struct apple_gmux_data *gmux_data = file->private_data; 578 + u32 data; 579 + 580 + data = gmux_read32(gmux_data, gmux_data->selected_port); 581 + 582 + return simple_read_from_buffer(userbuf, count, ppos, &data, sizeof(data)); 583 + } 584 + 585 + static const struct file_operations gmux_port_data_ops = { 586 + .open = simple_open, 587 + .write = gmux_selected_port_data_write, 588 + .read = gmux_selected_port_data_read 589 + }; 590 + 591 + static void gmux_init_debugfs(struct apple_gmux_data *gmux_data) 592 + { 593 + gmux_data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); 594 + 595 + debugfs_create_u8("selected_port", 0644, gmux_data->debug_dentry, 596 + &gmux_data->selected_port); 597 + debugfs_create_file("selected_port_data", 0644, gmux_data->debug_dentry, 598 + gmux_data, &gmux_port_data_ops); 599 + } 600 + 601 + static void gmux_fini_debugfs(struct apple_gmux_data *gmux_data) 602 + { 603 + debugfs_remove_recursive(gmux_data->debug_dentry); 676 604 } 677 605 678 606 static int gmux_suspend(struct device *dev) ··· 780 560 struct apple_gmux_data *gmux_data; 781 561 struct resource *res; 782 562 struct backlight_properties props; 783 - struct backlight_device *bdev; 563 + struct backlight_device *bdev = NULL; 784 564 u8 ver_major, ver_minor, ver_release; 565 + bool register_bdev = true; 785 566 int ret = -ENXIO; 786 567 acpi_status status; 787 568 unsigned long long gpe; 788 - bool indexed = false; 569 + enum apple_gmux_type type; 789 570 u32 version; 790 571 791 572 if (apple_gmux_data) 792 573 return -EBUSY; 793 574 794 - if (!apple_gmux_detect(pnp, &indexed)) { 575 + if (!apple_gmux_detect(pnp, &type)) { 795 576 pr_info("gmux device not present\n"); 796 577 return -ENODEV; 797 578 } ··· 801 580 if (!gmux_data) 802 581 return -ENOMEM; 803 582 pnp_set_drvdata(pnp, gmux_data); 583 + 584 + switch (type) { 585 + case APPLE_GMUX_TYPE_MMIO: 586 + gmux_data->config = &apple_gmux_mmio; 587 + mutex_init(&gmux_data->index_lock); 588 + 589 + res = pnp_get_resource(pnp, IORESOURCE_MEM, 0); 590 + gmux_data->iostart = res->start; 591 + /* Although the ACPI table only allocates 8 bytes, we need 16. */ 592 + gmux_data->iolen = 16; 593 + if (!request_mem_region(gmux_data->iostart, gmux_data->iolen, 594 + "Apple gmux")) { 595 + pr_err("gmux I/O already in use\n"); 596 + goto err_free; 597 + } 598 + gmux_data->iomem_base = ioremap(gmux_data->iostart, gmux_data->iolen); 599 + if (!gmux_data->iomem_base) { 600 + pr_err("couldn't remap gmux mmio region"); 601 + goto err_release; 602 + } 603 + goto get_version; 604 + case APPLE_GMUX_TYPE_INDEXED: 605 + gmux_data->config = &apple_gmux_index; 606 + mutex_init(&gmux_data->index_lock); 607 + break; 608 + case APPLE_GMUX_TYPE_PIO: 609 + gmux_data->config = &apple_gmux_pio; 610 + break; 611 + } 804 612 805 613 res = pnp_get_resource(pnp, IORESOURCE_IO, 0); 806 614 gmux_data->iostart = res->start; ··· 841 591 goto err_free; 842 592 } 843 593 844 - if (indexed) { 845 - mutex_init(&gmux_data->index_lock); 846 - gmux_data->indexed = true; 594 + get_version: 595 + if (gmux_data->config->read_version_as_u32) { 847 596 version = gmux_read32(gmux_data, GMUX_PORT_VERSION_MAJOR); 848 597 ver_major = (version >> 24) & 0xff; 849 598 ver_minor = (version >> 16) & 0xff; ··· 853 604 ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); 854 605 } 855 606 pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor, 856 - ver_release, (gmux_data->indexed ? "indexed" : "classic")); 607 + ver_release, gmux_data->config->name); 857 608 858 609 memset(&props, 0, sizeof(props)); 859 610 props.type = BACKLIGHT_PLATFORM; 860 611 props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS); 861 612 862 - /* 863 - * Currently it's assumed that the maximum brightness is less than 864 - * 2^24 for compatibility with old gmux versions. Cap the max 865 - * brightness at this value, but print a warning if the hardware 866 - * reports something higher so that it can be fixed. 867 - */ 868 - if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) 869 - props.max_brightness = GMUX_MAX_BRIGHTNESS; 613 + #if IS_REACHABLE(CONFIG_ACPI_VIDEO) 614 + register_bdev = acpi_video_get_backlight_type() == acpi_backlight_apple_gmux; 615 + #endif 616 + if (register_bdev) { 617 + /* 618 + * Currently it's assumed that the maximum brightness is less than 619 + * 2^24 for compatibility with old gmux versions. Cap the max 620 + * brightness at this value, but print a warning if the hardware 621 + * reports something higher so that it can be fixed. 622 + */ 623 + if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) 624 + props.max_brightness = GMUX_MAX_BRIGHTNESS; 870 625 871 - bdev = backlight_device_register("gmux_backlight", &pnp->dev, 872 - gmux_data, &gmux_bl_ops, &props); 873 - if (IS_ERR(bdev)) { 874 - ret = PTR_ERR(bdev); 875 - goto err_release; 626 + bdev = backlight_device_register("gmux_backlight", &pnp->dev, 627 + gmux_data, &gmux_bl_ops, &props); 628 + if (IS_ERR(bdev)) { 629 + ret = PTR_ERR(bdev); 630 + goto err_unmap; 631 + } 632 + 633 + gmux_data->bdev = bdev; 634 + bdev->props.brightness = gmux_get_brightness(bdev); 635 + backlight_update_status(bdev); 876 636 } 877 - 878 - gmux_data->bdev = bdev; 879 - bdev->props.brightness = gmux_get_brightness(bdev); 880 - backlight_update_status(bdev); 881 - 882 - /* 883 - * The backlight situation on Macs is complicated. If the gmux is 884 - * present it's the best choice, because it always works for 885 - * backlight control and supports more levels than other options. 886 - * Disable the other backlight choices. 887 - */ 888 - apple_bl_unregister(); 889 637 890 638 gmux_data->power_state = VGA_SWITCHEROO_ON; 891 639 ··· 936 690 /* 937 691 * Retina MacBook Pros cannot switch the panel's AUX separately 938 692 * and need eDP pre-calibration. They are distinguishable from 939 - * pre-retinas by having an "indexed" gmux. 693 + * pre-retinas by having an "indexed" or "T2" gmux. 940 694 * 941 695 * Pre-retina MacBook Pros can switch the panel's DDC separately. 942 696 */ 943 - if (gmux_data->indexed) 944 - ret = vga_switcheroo_register_handler(&gmux_handler_indexed, 945 - VGA_SWITCHEROO_NEEDS_EDP_CONFIG); 946 - else 947 - ret = vga_switcheroo_register_handler(&gmux_handler_classic, 948 - VGA_SWITCHEROO_CAN_SWITCH_DDC); 697 + ret = vga_switcheroo_register_handler(gmux_data->config->gmux_handler, 698 + gmux_data->config->handler_flags); 949 699 if (ret) { 950 700 pr_err("Failed to register vga_switcheroo handler\n"); 951 701 goto err_register_handler; 952 702 } 953 703 704 + gmux_init_debugfs(gmux_data); 954 705 return 0; 955 706 956 707 err_register_handler: ··· 962 719 &gmux_notify_handler); 963 720 err_notify: 964 721 backlight_device_unregister(bdev); 722 + err_unmap: 723 + if (gmux_data->iomem_base) 724 + iounmap(gmux_data->iomem_base); 965 725 err_release: 966 - release_region(gmux_data->iostart, gmux_data->iolen); 726 + if (gmux_data->config->resource_type == IORESOURCE_MEM) 727 + release_mem_region(gmux_data->iostart, gmux_data->iolen); 728 + else 729 + release_region(gmux_data->iostart, gmux_data->iolen); 967 730 err_free: 968 731 kfree(gmux_data); 969 732 return ret; ··· 979 730 { 980 731 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); 981 732 733 + gmux_fini_debugfs(gmux_data); 982 734 vga_switcheroo_unregister_handler(); 983 735 gmux_disable_interrupts(gmux_data); 984 736 if (gmux_data->gpe >= 0) { ··· 991 741 992 742 backlight_device_unregister(gmux_data->bdev); 993 743 994 - release_region(gmux_data->iostart, gmux_data->iolen); 744 + if (gmux_data->iomem_base) { 745 + iounmap(gmux_data->iomem_base); 746 + release_mem_region(gmux_data->iostart, gmux_data->iolen); 747 + } else 748 + release_region(gmux_data->iostart, gmux_data->iolen); 995 749 apple_gmux_data = NULL; 996 750 kfree(gmux_data); 997 - 998 - apple_bl_register(); 999 751 } 1000 752 1001 753 static const struct pnp_device_id gmux_device_ids[] = {
+2 -4
drivers/platform/x86/barco-p50-gpio.c
··· 370 370 return ret; 371 371 } 372 372 373 - static int p50_gpio_remove(struct platform_device *pdev) 373 + static void p50_gpio_remove(struct platform_device *pdev) 374 374 { 375 375 struct p50_gpio *p50 = platform_get_drvdata(pdev); 376 376 ··· 378 378 platform_device_unregister(p50->leds_pdev); 379 379 380 380 gpiod_remove_lookup_table(&p50_gpio_led_table); 381 - 382 - return 0; 383 381 } 384 382 385 383 static struct platform_driver p50_gpio_driver = { ··· 385 387 .name = DRIVER_NAME, 386 388 }, 387 389 .probe = p50_gpio_probe, 388 - .remove = p50_gpio_remove, 390 + .remove_new = p50_gpio_remove, 389 391 }; 390 392 391 393 /* Board setup */
+1 -1
drivers/platform/x86/classmate-laptop.c
··· 1134 1134 module_init(cmpc_init); 1135 1135 module_exit(cmpc_exit); 1136 1136 1137 - static const struct acpi_device_id cmpc_device_ids[] = { 1137 + static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = { 1138 1138 {CMPC_ACCEL_HID, 0}, 1139 1139 {CMPC_ACCEL_HID_V4, 0}, 1140 1140 {CMPC_TABLET_HID, 0},
+3 -5
drivers/platform/x86/compal-laptop.c
··· 1003 1003 return err; 1004 1004 } 1005 1005 1006 - static int compal_remove(struct platform_device *pdev) 1006 + static void compal_remove(struct platform_device *pdev) 1007 1007 { 1008 1008 struct compal_data *data; 1009 1009 1010 1010 if (!extra_features) 1011 - return 0; 1011 + return; 1012 1012 1013 1013 pr_info("Unloading: resetting fan control to motherboard\n"); 1014 1014 pwm_disable_control(); ··· 1017 1017 power_supply_unregister(data->psy); 1018 1018 1019 1019 sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group); 1020 - 1021 - return 0; 1022 1020 } 1023 1021 1024 1022 static struct platform_driver compal_driver = { ··· 1024 1026 .name = DRIVER_NAME, 1025 1027 }, 1026 1028 .probe = compal_probe, 1027 - .remove = compal_remove, 1029 + .remove_new = compal_remove, 1028 1030 }; 1029 1031 1030 1032 static int __init compal_init(void)
+2 -4
drivers/platform/x86/dell/dcdbas.c
··· 698 698 return 0; 699 699 } 700 700 701 - static int dcdbas_remove(struct platform_device *dev) 701 + static void dcdbas_remove(struct platform_device *dev) 702 702 { 703 703 unregister_reboot_notifier(&dcdbas_reboot_nb); 704 704 sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); 705 - 706 - return 0; 707 705 } 708 706 709 707 static struct platform_driver dcdbas_driver = { ··· 709 711 .name = DRIVER_NAME, 710 712 }, 711 713 .probe = dcdbas_probe, 712 - .remove = dcdbas_remove, 714 + .remove_new = dcdbas_remove, 713 715 }; 714 716 715 717 static const struct platform_device_info dcdbas_dev_info __initconst = {
+42
drivers/platform/x86/dell/dell-laptop.c
··· 97 97 static struct rfkill *wwan_rfkill; 98 98 static bool force_rfkill; 99 99 static bool micmute_led_registered; 100 + static bool mute_led_registered; 100 101 101 102 module_param(force_rfkill, bool, 0444); 102 103 MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models"); ··· 2178 2177 .default_trigger = "audio-micmute", 2179 2178 }; 2180 2179 2180 + static int mute_led_set(struct led_classdev *led_cdev, 2181 + enum led_brightness brightness) 2182 + { 2183 + struct calling_interface_buffer buffer; 2184 + struct calling_interface_token *token; 2185 + int state = brightness != LED_OFF; 2186 + 2187 + if (state == 0) 2188 + token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE); 2189 + else 2190 + token = dell_smbios_find_token(GLOBAL_MUTE_ENABLE); 2191 + 2192 + if (!token) 2193 + return -ENODEV; 2194 + 2195 + dell_fill_request(&buffer, token->location, token->value, 0, 0); 2196 + dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); 2197 + 2198 + return 0; 2199 + } 2200 + 2201 + static struct led_classdev mute_led_cdev = { 2202 + .name = "platform::mute", 2203 + .max_brightness = 1, 2204 + .brightness_set_blocking = mute_led_set, 2205 + .default_trigger = "audio-mute", 2206 + }; 2207 + 2181 2208 static int __init dell_init(void) 2182 2209 { 2183 2210 struct calling_interface_token *token; ··· 2259 2230 micmute_led_registered = true; 2260 2231 } 2261 2232 2233 + if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) && 2234 + dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) { 2235 + mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE); 2236 + ret = led_classdev_register(&platform_device->dev, &mute_led_cdev); 2237 + if (ret < 0) 2238 + goto fail_backlight; 2239 + mute_led_registered = true; 2240 + } 2241 + 2262 2242 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 2263 2243 return 0; 2264 2244 ··· 2315 2277 fail_backlight: 2316 2278 if (micmute_led_registered) 2317 2279 led_classdev_unregister(&micmute_led_cdev); 2280 + if (mute_led_registered) 2281 + led_classdev_unregister(&mute_led_cdev); 2318 2282 fail_led: 2319 2283 dell_cleanup_rfkill(); 2320 2284 fail_rfkill: ··· 2339 2299 backlight_device_unregister(dell_backlight_device); 2340 2300 if (micmute_led_registered) 2341 2301 led_classdev_unregister(&micmute_led_cdev); 2302 + if (mute_led_registered) 2303 + led_classdev_unregister(&mute_led_cdev); 2342 2304 dell_cleanup_rfkill(); 2343 2305 if (platform_device) { 2344 2306 platform_device_unregister(platform_device);
+2
drivers/platform/x86/dell/dell-smbios.h
··· 34 34 #define KBD_LED_AUTO_100_TOKEN 0x02F6 35 35 #define GLOBAL_MIC_MUTE_ENABLE 0x0364 36 36 #define GLOBAL_MIC_MUTE_DISABLE 0x0365 37 + #define GLOBAL_MUTE_ENABLE 0x058C 38 + #define GLOBAL_MUTE_DISABLE 0x058D 37 39 38 40 struct notifier_block; 39 41
+2 -3
drivers/platform/x86/dell/dell-smo8800.c
··· 154 154 return err; 155 155 } 156 156 157 - static int smo8800_remove(struct platform_device *device) 157 + static void smo8800_remove(struct platform_device *device) 158 158 { 159 159 struct smo8800_device *smo8800 = platform_get_drvdata(device); 160 160 161 161 free_irq(smo8800->irq, smo8800); 162 162 misc_deregister(&smo8800->miscdev); 163 163 dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); 164 - return 0; 165 164 } 166 165 167 166 /* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */ ··· 179 180 180 181 static struct platform_driver smo8800_driver = { 181 182 .probe = smo8800_probe, 182 - .remove = smo8800_remove, 183 + .remove_new = smo8800_remove, 183 184 .driver = { 184 185 .name = DRIVER_NAME, 185 186 .acpi_match_table = smo8800_ids,
+2 -3
drivers/platform/x86/hp/hp_accel.c
··· 342 342 return ret; 343 343 } 344 344 345 - static int lis3lv02d_remove(struct platform_device *device) 345 + static void lis3lv02d_remove(struct platform_device *device) 346 346 { 347 347 i8042_remove_filter(hp_accel_i8042_filter); 348 348 lis3lv02d_joystick_disable(&lis3_dev); ··· 352 352 flush_work(&hpled_led.work); 353 353 354 354 lis3lv02d_remove_fs(&lis3_dev); 355 - return 0; 356 355 } 357 356 358 357 static int __maybe_unused lis3lv02d_suspend(struct device *dev) ··· 372 373 /* For the HP MDPS aka 3D Driveguard */ 373 374 static struct platform_driver lis3lv02d_driver = { 374 375 .probe = lis3lv02d_probe, 375 - .remove = lis3lv02d_remove, 376 + .remove_new = lis3lv02d_remove, 376 377 .driver = { 377 378 .name = "hp_accel", 378 379 .pm = &hp_accel_pm,
+2 -4
drivers/platform/x86/hp/tc1100-wmi.c
··· 170 170 } 171 171 172 172 173 - static int tc1100_remove(struct platform_device *device) 173 + static void tc1100_remove(struct platform_device *device) 174 174 { 175 175 sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); 176 - 177 - return 0; 178 176 } 179 177 180 178 #ifdef CONFIG_PM ··· 221 223 .pm = &tc1100_pm_ops, 222 224 #endif 223 225 }, 224 - .remove = tc1100_remove, 226 + .remove_new = tc1100_remove, 225 227 }; 226 228 227 229 static int __init tc1100_init(void)
+2 -4
drivers/platform/x86/huawei-wmi.c
··· 830 830 return 0; 831 831 } 832 832 833 - static int huawei_wmi_remove(struct platform_device *pdev) 833 + static void huawei_wmi_remove(struct platform_device *pdev) 834 834 { 835 835 const struct wmi_device_id *guid = huawei_wmi_events_id_table; 836 836 ··· 846 846 huawei_wmi_battery_exit(&pdev->dev); 847 847 huawei_wmi_fn_lock_exit(&pdev->dev); 848 848 } 849 - 850 - return 0; 851 849 } 852 850 853 851 static struct platform_driver huawei_wmi_driver = { ··· 853 855 .name = "huawei-wmi", 854 856 }, 855 857 .probe = huawei_wmi_probe, 856 - .remove = huawei_wmi_remove, 858 + .remove_new = huawei_wmi_remove, 857 859 }; 858 860 859 861 static __init int huawei_wmi_init(void)
+3 -138
drivers/platform/x86/ideapad-laptop.c
··· 20 20 #include <linux/init.h> 21 21 #include <linux/input.h> 22 22 #include <linux/input/sparse-keymap.h> 23 - #include <linux/jiffies.h> 24 23 #include <linux/kernel.h> 25 24 #include <linux/leds.h> 26 25 #include <linux/module.h> ··· 30 31 #include <linux/sysfs.h> 31 32 #include <linux/types.h> 32 33 #include <linux/wmi.h> 34 + #include "ideapad-laptop.h" 33 35 34 36 #include <acpi/video.h> 35 37 ··· 83 83 SALS_USB_CHARGING_OFF = 0xb, 84 84 SALS_FNLOCK_ON = 0xe, 85 85 SALS_FNLOCK_OFF = 0xf, 86 - }; 87 - 88 - enum { 89 - VPCCMD_R_VPC1 = 0x10, 90 - VPCCMD_R_BL_MAX, 91 - VPCCMD_R_BL, 92 - VPCCMD_W_BL, 93 - VPCCMD_R_WIFI, 94 - VPCCMD_W_WIFI, 95 - VPCCMD_R_BT, 96 - VPCCMD_W_BT, 97 - VPCCMD_R_BL_POWER, 98 - VPCCMD_R_NOVO, 99 - VPCCMD_R_VPC2, 100 - VPCCMD_R_TOUCHPAD, 101 - VPCCMD_W_TOUCHPAD, 102 - VPCCMD_R_CAMERA, 103 - VPCCMD_W_CAMERA, 104 - VPCCMD_R_3G, 105 - VPCCMD_W_3G, 106 - VPCCMD_R_ODD, /* 0x21 */ 107 - VPCCMD_W_FAN, 108 - VPCCMD_R_RF, 109 - VPCCMD_W_RF, 110 - VPCCMD_R_FAN = 0x2B, 111 - VPCCMD_R_SPECIAL_BUTTONS = 0x31, 112 - VPCCMD_W_BL_POWER = 0x33, 113 86 }; 114 87 115 88 struct ideapad_dytc_priv { ··· 200 227 /* 201 228 * ACPI Helpers 202 229 */ 203 - #define IDEAPAD_EC_TIMEOUT 200 /* in ms */ 204 230 205 231 static int eval_int(acpi_handle handle, const char *name, unsigned long *res) 206 232 { ··· 242 270 return exec_simple_method(handle, "SALS", arg); 243 271 } 244 272 245 - static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res) 246 - { 247 - struct acpi_object_list params; 248 - unsigned long long result; 249 - union acpi_object in_obj; 250 - acpi_status status; 251 - 252 - params.count = 1; 253 - params.pointer = &in_obj; 254 - in_obj.type = ACPI_TYPE_INTEGER; 255 - in_obj.integer.value = arg; 256 - 257 - status = acpi_evaluate_integer(handle, (char *)name, &params, &result); 258 - if (ACPI_FAILURE(status)) 259 - return -EIO; 260 - 261 - if (res) 262 - *res = result; 263 - 264 - return 0; 265 - } 266 - 267 273 static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res) 268 274 { 269 275 return eval_int_with_arg(handle, "DYTC", cmd, res); 270 - } 271 - 272 - static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res) 273 - { 274 - return eval_int_with_arg(handle, "VPCR", cmd, res); 275 - } 276 - 277 - static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data) 278 - { 279 - struct acpi_object_list params; 280 - union acpi_object in_obj[2]; 281 - acpi_status status; 282 - 283 - params.count = 2; 284 - params.pointer = in_obj; 285 - in_obj[0].type = ACPI_TYPE_INTEGER; 286 - in_obj[0].integer.value = cmd; 287 - in_obj[1].type = ACPI_TYPE_INTEGER; 288 - in_obj[1].integer.value = data; 289 - 290 - status = acpi_evaluate_object(handle, "VPCW", &params, NULL); 291 - if (ACPI_FAILURE(status)) 292 - return -EIO; 293 - 294 - return 0; 295 - } 296 - 297 - static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data) 298 - { 299 - unsigned long end_jiffies, val; 300 - int err; 301 - 302 - err = eval_vpcw(handle, 1, cmd); 303 - if (err) 304 - return err; 305 - 306 - end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; 307 - 308 - while (time_before(jiffies, end_jiffies)) { 309 - schedule(); 310 - 311 - err = eval_vpcr(handle, 1, &val); 312 - if (err) 313 - return err; 314 - 315 - if (val == 0) 316 - return eval_vpcr(handle, 0, data); 317 - } 318 - 319 - acpi_handle_err(handle, "timeout in %s\n", __func__); 320 - 321 - return -ETIMEDOUT; 322 - } 323 - 324 - static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data) 325 - { 326 - unsigned long end_jiffies, val; 327 - int err; 328 - 329 - err = eval_vpcw(handle, 0, data); 330 - if (err) 331 - return err; 332 - 333 - err = eval_vpcw(handle, 1, cmd); 334 - if (err) 335 - return err; 336 - 337 - end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; 338 - 339 - while (time_before(jiffies, end_jiffies)) { 340 - schedule(); 341 - 342 - err = eval_vpcr(handle, 1, &val); 343 - if (err) 344 - return err; 345 - 346 - if (val == 0) 347 - return 0; 348 - } 349 - 350 - acpi_handle_err(handle, "timeout in %s\n", __func__); 351 - 352 - return -ETIMEDOUT; 353 276 } 354 277 355 278 /* ··· 1785 1918 return err; 1786 1919 } 1787 1920 1788 - static int ideapad_acpi_remove(struct platform_device *pdev) 1921 + static void ideapad_acpi_remove(struct platform_device *pdev) 1789 1922 { 1790 1923 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev); 1791 1924 int i; ··· 1806 1939 ideapad_input_exit(priv); 1807 1940 ideapad_debugfs_exit(priv); 1808 1941 ideapad_sysfs_exit(priv); 1809 - 1810 - return 0; 1811 1942 } 1812 1943 1813 1944 #ifdef CONFIG_PM_SLEEP ··· 1832 1967 1833 1968 static struct platform_driver ideapad_acpi_driver = { 1834 1969 .probe = ideapad_acpi_add, 1835 - .remove = ideapad_acpi_remove, 1970 + .remove_new = ideapad_acpi_remove, 1836 1971 .driver = { 1837 1972 .name = "ideapad_acpi", 1838 1973 .pm = &ideapad_pm,
+152
drivers/platform/x86/ideapad-laptop.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * ideapad-laptop.h - Lenovo IdeaPad ACPI Extras 4 + * 5 + * Copyright © 2010 Intel Corporation 6 + * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> 7 + */ 8 + 9 + #ifndef _IDEAPAD_LAPTOP_H_ 10 + #define _IDEAPAD_LAPTOP_H_ 11 + 12 + #include <linux/acpi.h> 13 + #include <linux/jiffies.h> 14 + #include <linux/errno.h> 15 + 16 + enum { 17 + VPCCMD_R_VPC1 = 0x10, 18 + VPCCMD_R_BL_MAX, 19 + VPCCMD_R_BL, 20 + VPCCMD_W_BL, 21 + VPCCMD_R_WIFI, 22 + VPCCMD_W_WIFI, 23 + VPCCMD_R_BT, 24 + VPCCMD_W_BT, 25 + VPCCMD_R_BL_POWER, 26 + VPCCMD_R_NOVO, 27 + VPCCMD_R_VPC2, 28 + VPCCMD_R_TOUCHPAD, 29 + VPCCMD_W_TOUCHPAD, 30 + VPCCMD_R_CAMERA, 31 + VPCCMD_W_CAMERA, 32 + VPCCMD_R_3G, 33 + VPCCMD_W_3G, 34 + VPCCMD_R_ODD, /* 0x21 */ 35 + VPCCMD_W_FAN, 36 + VPCCMD_R_RF, 37 + VPCCMD_W_RF, 38 + VPCCMD_W_YMC = 0x2A, 39 + VPCCMD_R_FAN = 0x2B, 40 + VPCCMD_R_SPECIAL_BUTTONS = 0x31, 41 + VPCCMD_W_BL_POWER = 0x33, 42 + }; 43 + 44 + static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res) 45 + { 46 + struct acpi_object_list params; 47 + unsigned long long result; 48 + union acpi_object in_obj; 49 + acpi_status status; 50 + 51 + params.count = 1; 52 + params.pointer = &in_obj; 53 + in_obj.type = ACPI_TYPE_INTEGER; 54 + in_obj.integer.value = arg; 55 + 56 + status = acpi_evaluate_integer(handle, (char *)name, &params, &result); 57 + if (ACPI_FAILURE(status)) 58 + return -EIO; 59 + 60 + if (res) 61 + *res = result; 62 + 63 + return 0; 64 + } 65 + 66 + static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res) 67 + { 68 + return eval_int_with_arg(handle, "VPCR", cmd, res); 69 + } 70 + 71 + static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data) 72 + { 73 + struct acpi_object_list params; 74 + union acpi_object in_obj[2]; 75 + acpi_status status; 76 + 77 + params.count = 2; 78 + params.pointer = in_obj; 79 + in_obj[0].type = ACPI_TYPE_INTEGER; 80 + in_obj[0].integer.value = cmd; 81 + in_obj[1].type = ACPI_TYPE_INTEGER; 82 + in_obj[1].integer.value = data; 83 + 84 + status = acpi_evaluate_object(handle, "VPCW", &params, NULL); 85 + if (ACPI_FAILURE(status)) 86 + return -EIO; 87 + 88 + return 0; 89 + } 90 + 91 + #define IDEAPAD_EC_TIMEOUT 200 /* in ms */ 92 + 93 + static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data) 94 + { 95 + unsigned long end_jiffies, val; 96 + int err; 97 + 98 + err = eval_vpcw(handle, 1, cmd); 99 + if (err) 100 + return err; 101 + 102 + end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; 103 + 104 + while (time_before(jiffies, end_jiffies)) { 105 + schedule(); 106 + 107 + err = eval_vpcr(handle, 1, &val); 108 + if (err) 109 + return err; 110 + 111 + if (val == 0) 112 + return eval_vpcr(handle, 0, data); 113 + } 114 + 115 + acpi_handle_err(handle, "timeout in %s\n", __func__); 116 + 117 + return -ETIMEDOUT; 118 + } 119 + 120 + static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data) 121 + { 122 + unsigned long end_jiffies, val; 123 + int err; 124 + 125 + err = eval_vpcw(handle, 0, data); 126 + if (err) 127 + return err; 128 + 129 + err = eval_vpcw(handle, 1, cmd); 130 + if (err) 131 + return err; 132 + 133 + end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; 134 + 135 + while (time_before(jiffies, end_jiffies)) { 136 + schedule(); 137 + 138 + err = eval_vpcr(handle, 1, &val); 139 + if (err) 140 + return err; 141 + 142 + if (val == 0) 143 + return 0; 144 + } 145 + 146 + acpi_handle_err(handle, "timeout in %s\n", __func__); 147 + 148 + return -ETIMEDOUT; 149 + } 150 + 151 + #undef IDEAPAD_EC_TIMEOUT 152 + #endif /* !_IDEAPAD_LAPTOP_H_ */
+10
drivers/platform/x86/intel/Kconfig
··· 80 80 This driver enables the alarm wakeup functionality in the TMU unit of 81 81 Whiskey Cove PMIC. 82 82 83 + config INTEL_BYTCRC_PWRSRC 84 + tristate "Intel Bay Trail Crystal Cove power source driver" 85 + depends on INTEL_SOC_PMIC 86 + help 87 + This option adds a power source driver for Crystal Cove PMICs 88 + on Intel Bay Trail devices. 89 + 90 + To compile this driver as a module, choose M here: the module 91 + will be called intel_bytcrc_pwrsrc. 92 + 83 93 config INTEL_CHTDC_TI_PWRBTN 84 94 tristate "Intel Cherry Trail Dollar Cove TI power button driver" 85 95 depends on INTEL_SOC_PMIC_CHTDC_TI
+2
drivers/platform/x86/intel/Makefile
··· 38 38 obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o 39 39 intel_crystal_cove_charger-y := crystal_cove_charger.o 40 40 obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o 41 + intel_bytcrc_pwrsrc-y := bytcrc_pwrsrc.o 42 + obj-$(CONFIG_INTEL_BYTCRC_PWRSRC) += intel_bytcrc_pwrsrc.o 41 43 intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o 42 44 obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o 43 45 intel_chtwc_int33fe-y := chtwc_int33fe.o
+2 -3
drivers/platform/x86/intel/bxtwc_tmu.c
··· 89 89 return 0; 90 90 } 91 91 92 - static int bxt_wcove_tmu_remove(struct platform_device *pdev) 92 + static void bxt_wcove_tmu_remove(struct platform_device *pdev) 93 93 { 94 94 struct wcove_tmu *wctmu = platform_get_drvdata(pdev); 95 95 unsigned int val; ··· 101 101 regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val); 102 102 regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG, 103 103 val | BXTWC_TMU_ALRM_MASK); 104 - return 0; 105 104 } 106 105 107 106 #ifdef CONFIG_PM_SLEEP ··· 131 132 132 133 static struct platform_driver bxt_wcove_tmu_driver = { 133 134 .probe = bxt_wcove_tmu_probe, 134 - .remove = bxt_wcove_tmu_remove, 135 + .remove_new = bxt_wcove_tmu_remove, 135 136 .driver = { 136 137 .name = "bxt_wcove_tmu", 137 138 .pm = &bxtwc_tmu_pm_ops,
+181
drivers/platform/x86/intel/bytcrc_pwrsrc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Power-source driver for Bay Trail Crystal Cove PMIC 4 + * 5 + * Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com> 6 + * 7 + * Based on intel_crystalcove_pwrsrc.c from Android kernel sources, which is: 8 + * Copyright (C) 2013 Intel Corporation 9 + */ 10 + 11 + #include <linux/debugfs.h> 12 + #include <linux/mfd/intel_soc_pmic.h> 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/regmap.h> 16 + 17 + #define CRYSTALCOVE_SPWRSRC_REG 0x1E 18 + #define CRYSTALCOVE_RESETSRC0_REG 0x20 19 + #define CRYSTALCOVE_RESETSRC1_REG 0x21 20 + #define CRYSTALCOVE_WAKESRC_REG 0x22 21 + 22 + struct crc_pwrsrc_data { 23 + struct regmap *regmap; 24 + struct dentry *debug_dentry; 25 + unsigned int resetsrc0; 26 + unsigned int resetsrc1; 27 + unsigned int wakesrc; 28 + }; 29 + 30 + static const char * const pwrsrc_pwrsrc_info[] = { 31 + /* bit 0 */ "USB", 32 + /* bit 1 */ "DC in", 33 + /* bit 2 */ "Battery", 34 + NULL, 35 + }; 36 + 37 + static const char * const pwrsrc_resetsrc0_info[] = { 38 + /* bit 0 */ "SOC reporting a thermal event", 39 + /* bit 1 */ "critical PMIC temperature", 40 + /* bit 2 */ "critical system temperature", 41 + /* bit 3 */ "critical battery temperature", 42 + /* bit 4 */ "VSYS under voltage", 43 + /* bit 5 */ "VSYS over voltage", 44 + /* bit 6 */ "battery removal", 45 + NULL, 46 + }; 47 + 48 + static const char * const pwrsrc_resetsrc1_info[] = { 49 + /* bit 0 */ "VCRIT threshold", 50 + /* bit 1 */ "BATID reporting battery removal", 51 + /* bit 2 */ "user pressing the power button", 52 + NULL, 53 + }; 54 + 55 + static const char * const pwrsrc_wakesrc_info[] = { 56 + /* bit 0 */ "user pressing the power button", 57 + /* bit 1 */ "a battery insertion", 58 + /* bit 2 */ "a USB charger insertion", 59 + /* bit 3 */ "an adapter insertion", 60 + NULL, 61 + }; 62 + 63 + static void crc_pwrsrc_log(struct seq_file *seq, const char *prefix, 64 + const char * const *info, unsigned int reg_val) 65 + { 66 + int i; 67 + 68 + for (i = 0; info[i]; i++) { 69 + if (reg_val & BIT(i)) 70 + seq_printf(seq, "%s by %s\n", prefix, info[i]); 71 + } 72 + } 73 + 74 + static int pwrsrc_show(struct seq_file *seq, void *unused) 75 + { 76 + struct crc_pwrsrc_data *data = seq->private; 77 + unsigned int reg_val; 78 + int ret; 79 + 80 + ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &reg_val); 81 + if (ret) 82 + return ret; 83 + 84 + crc_pwrsrc_log(seq, "System powered", pwrsrc_pwrsrc_info, reg_val); 85 + return 0; 86 + } 87 + 88 + static int resetsrc_show(struct seq_file *seq, void *unused) 89 + { 90 + struct crc_pwrsrc_data *data = seq->private; 91 + 92 + crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc0_info, data->resetsrc0); 93 + crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc1_info, data->resetsrc1); 94 + return 0; 95 + } 96 + 97 + static int wakesrc_show(struct seq_file *seq, void *unused) 98 + { 99 + struct crc_pwrsrc_data *data = seq->private; 100 + 101 + crc_pwrsrc_log(seq, "Last wake caused", pwrsrc_wakesrc_info, data->wakesrc); 102 + return 0; 103 + } 104 + 105 + DEFINE_SHOW_ATTRIBUTE(pwrsrc); 106 + DEFINE_SHOW_ATTRIBUTE(resetsrc); 107 + DEFINE_SHOW_ATTRIBUTE(wakesrc); 108 + 109 + static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data, 110 + unsigned int reg, unsigned int *val) 111 + { 112 + int ret; 113 + 114 + ret = regmap_read(data->regmap, reg, val); 115 + if (ret) 116 + return ret; 117 + 118 + return regmap_write(data->regmap, reg, *val); 119 + } 120 + 121 + static int crc_pwrsrc_probe(struct platform_device *pdev) 122 + { 123 + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); 124 + struct crc_pwrsrc_data *data; 125 + int ret; 126 + 127 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 128 + if (!data) 129 + return -ENOMEM; 130 + 131 + data->regmap = pmic->regmap; 132 + 133 + /* 134 + * Read + clear resetsrc0/1 and wakesrc now, so that they get 135 + * cleared even if the debugfs interface is never used. 136 + * 137 + * Properly clearing the wakesrc is important, leaving bit 0 of it 138 + * set turns reboot into poweroff on some tablets. 139 + */ 140 + ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC0_REG, &data->resetsrc0); 141 + if (ret) 142 + return ret; 143 + 144 + ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC1_REG, &data->resetsrc1); 145 + if (ret) 146 + return ret; 147 + 148 + ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_WAKESRC_REG, &data->wakesrc); 149 + if (ret) 150 + return ret; 151 + 152 + data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); 153 + debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops); 154 + debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops); 155 + debugfs_create_file("wakesrc", 0444, data->debug_dentry, data, &wakesrc_fops); 156 + 157 + platform_set_drvdata(pdev, data); 158 + return 0; 159 + } 160 + 161 + static int crc_pwrsrc_remove(struct platform_device *pdev) 162 + { 163 + struct crc_pwrsrc_data *data = platform_get_drvdata(pdev); 164 + 165 + debugfs_remove_recursive(data->debug_dentry); 166 + return 0; 167 + } 168 + 169 + static struct platform_driver crc_pwrsrc_driver = { 170 + .probe = crc_pwrsrc_probe, 171 + .remove = crc_pwrsrc_remove, 172 + .driver = { 173 + .name = "crystal_cove_pwrsrc", 174 + }, 175 + }; 176 + module_platform_driver(crc_pwrsrc_driver); 177 + 178 + MODULE_ALIAS("platform:crystal_cove_pwrsrc"); 179 + MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 180 + MODULE_DESCRIPTION("Power-source driver for Bay Trail Crystal Cove PMIC"); 181 + MODULE_LICENSE("GPL");
+2 -3
drivers/platform/x86/intel/chtdc_ti_pwrbtn.c
··· 67 67 return 0; 68 68 } 69 69 70 - static int chtdc_ti_pwrbtn_remove(struct platform_device *pdev) 70 + static void chtdc_ti_pwrbtn_remove(struct platform_device *pdev) 71 71 { 72 72 dev_pm_clear_wake_irq(&pdev->dev); 73 73 device_init_wakeup(&pdev->dev, false); 74 - return 0; 75 74 } 76 75 77 76 static const struct platform_device_id chtdc_ti_pwrbtn_id_table[] = { ··· 84 85 .name = KBUILD_MODNAME, 85 86 }, 86 87 .probe = chtdc_ti_pwrbtn_probe, 87 - .remove = chtdc_ti_pwrbtn_remove, 88 + .remove_new = chtdc_ti_pwrbtn_remove, 88 89 .id_table = chtdc_ti_pwrbtn_id_table, 89 90 }; 90 91 module_platform_driver(chtdc_ti_pwrbtn_driver);
+2 -4
drivers/platform/x86/intel/chtwc_int33fe.c
··· 405 405 return ret; 406 406 } 407 407 408 - static int cht_int33fe_typec_remove(struct platform_device *pdev) 408 + static void cht_int33fe_typec_remove(struct platform_device *pdev) 409 409 { 410 410 struct cht_int33fe_data *data = platform_get_drvdata(pdev); 411 411 ··· 414 414 i2c_unregister_device(data->battery_fg); 415 415 416 416 cht_int33fe_remove_nodes(data); 417 - 418 - return 0; 419 417 } 420 418 421 419 static const struct acpi_device_id cht_int33fe_acpi_ids[] = { ··· 427 429 .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), 428 430 }, 429 431 .probe = cht_int33fe_typec_probe, 430 - .remove = cht_int33fe_typec_remove, 432 + .remove_new = cht_int33fe_typec_remove, 431 433 }; 432 434 433 435 module_platform_driver(cht_int33fe_typec_driver);
+2 -8
drivers/platform/x86/intel/hid.c
··· 720 720 return err; 721 721 } 722 722 723 - static int intel_hid_remove(struct platform_device *device) 723 + static void intel_hid_remove(struct platform_device *device) 724 724 { 725 725 acpi_handle handle = ACPI_HANDLE(&device->dev); 726 726 ··· 728 728 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); 729 729 intel_hid_set_enable(&device->dev, false); 730 730 intel_button_array_enable(&device->dev, false); 731 - 732 - /* 733 - * Even if we failed to shut off the event stream, we can still 734 - * safely detach from the device. 735 - */ 736 - return 0; 737 731 } 738 732 739 733 static struct platform_driver intel_hid_pl_driver = { ··· 737 743 .pm = &intel_hid_pl_pm_ops, 738 744 }, 739 745 .probe = intel_hid_probe, 740 - .remove = intel_hid_remove, 746 + .remove_new = intel_hid_remove, 741 747 }; 742 748 743 749 /*
+58 -23
drivers/platform/x86/intel/ifs/core.c
··· 16 16 17 17 static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { 18 18 X86_MATCH(SAPPHIRERAPIDS_X), 19 + X86_MATCH(EMERALDRAPIDS_X), 19 20 {} 20 21 }; 21 22 MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); 22 23 23 - static struct ifs_device ifs_device = { 24 - .data = { 25 - .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, 26 - .test_num = 0, 24 + ATTRIBUTE_GROUPS(plat_ifs); 25 + ATTRIBUTE_GROUPS(plat_ifs_array); 26 + 27 + bool *ifs_pkg_auth; 28 + 29 + static const struct ifs_test_caps scan_test = { 30 + .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, 31 + .test_num = IFS_TYPE_SAF, 32 + }; 33 + 34 + static const struct ifs_test_caps array_test = { 35 + .integrity_cap_bit = MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT, 36 + .test_num = IFS_TYPE_ARRAY_BIST, 37 + }; 38 + 39 + static struct ifs_device ifs_devices[] = { 40 + [IFS_TYPE_SAF] = { 41 + .test_caps = &scan_test, 42 + .misc = { 43 + .name = "intel_ifs_0", 44 + .minor = MISC_DYNAMIC_MINOR, 45 + .groups = plat_ifs_groups, 46 + }, 27 47 }, 28 - .misc = { 29 - .name = "intel_ifs_0", 30 - .nodename = "intel_ifs/0", 31 - .minor = MISC_DYNAMIC_MINOR, 48 + [IFS_TYPE_ARRAY_BIST] = { 49 + .test_caps = &array_test, 50 + .misc = { 51 + .name = "intel_ifs_1", 52 + .minor = MISC_DYNAMIC_MINOR, 53 + .groups = plat_ifs_array_groups, 54 + }, 32 55 }, 33 56 }; 57 + 58 + #define IFS_NUMTESTS ARRAY_SIZE(ifs_devices) 59 + 60 + static void ifs_cleanup(void) 61 + { 62 + int i; 63 + 64 + for (i = 0; i < IFS_NUMTESTS; i++) { 65 + if (ifs_devices[i].misc.this_device) 66 + misc_deregister(&ifs_devices[i].misc); 67 + } 68 + kfree(ifs_pkg_auth); 69 + } 34 70 35 71 static int __init ifs_init(void) 36 72 { 37 73 const struct x86_cpu_id *m; 38 74 u64 msrval; 39 - int ret; 75 + int i, ret; 40 76 41 77 m = x86_match_cpu(ifs_cpu_ids); 42 78 if (!m) ··· 87 51 if (rdmsrl_safe(MSR_INTEGRITY_CAPS, &msrval)) 88 52 return -ENODEV; 89 53 90 - ifs_device.misc.groups = ifs_get_groups(); 91 - 92 - if (!(msrval & BIT(ifs_device.data.integrity_cap_bit))) 93 - return -ENODEV; 94 - 95 - ifs_device.data.pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); 96 - if (!ifs_device.data.pkg_auth) 54 + ifs_pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); 55 + if (!ifs_pkg_auth) 97 56 return -ENOMEM; 98 57 99 - ret = misc_register(&ifs_device.misc); 100 - if (ret) { 101 - kfree(ifs_device.data.pkg_auth); 102 - return ret; 58 + for (i = 0; i < IFS_NUMTESTS; i++) { 59 + if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit))) 60 + continue; 61 + ret = misc_register(&ifs_devices[i].misc); 62 + if (ret) 63 + goto err_exit; 103 64 } 104 - 105 65 return 0; 66 + 67 + err_exit: 68 + ifs_cleanup(); 69 + return ret; 106 70 } 107 71 108 72 static void __exit ifs_exit(void) 109 73 { 110 - misc_deregister(&ifs_device.misc); 111 - kfree(ifs_device.data.pkg_auth); 74 + ifs_cleanup(); 112 75 } 113 76 114 77 module_init(ifs_init);
+48 -20
drivers/platform/x86/intel/ifs/ifs.h
··· 17 17 * In Field Scan (IFS) is a hardware feature to run circuit level tests on 18 18 * a CPU core to detect problems that are not caught by parity or ECC checks. 19 19 * Future CPUs will support more than one type of test which will show up 20 - * with a new platform-device instance-id, for now only .0 is exposed. 20 + * with a new platform-device instance-id. 21 21 * 22 22 * 23 23 * IFS Image ··· 25 25 * 26 26 * Intel provides a firmware file containing the scan tests via 27 27 * github [#f1]_. Similar to microcode there is a separate file for each 28 - * family-model-stepping. 28 + * family-model-stepping. IFS Images are not applicable for some test types. 29 + * Wherever applicable the sysfs directory would provide a "current_batch" file 30 + * (see below) for loading the image. 31 + * 29 32 * 30 33 * IFS Image Loading 31 34 * ----------------- ··· 38 35 * SHA hashes for the test. Then the tests themselves. Status MSRs provide 39 36 * feedback on the success/failure of these steps. 40 37 * 41 - * The test files are kept in a fixed location: /lib/firmware/intel/ifs_0/ 38 + * The test files are kept in a fixed location: /lib/firmware/intel/ifs_<n>/ 42 39 * For e.g if there are 3 test files, they would be named in the following 43 40 * fashion: 44 41 * ff-mm-ss-01.scan ··· 50 47 * (e.g 1, 2 or 3 in the above scenario) into the curent_batch file. 51 48 * To load ff-mm-ss-02.scan, the following command can be used:: 52 49 * 53 - * # echo 2 > /sys/devices/virtual/misc/intel_ifs_0/current_batch 50 + * # echo 2 > /sys/devices/virtual/misc/intel_ifs_<n>/current_batch 54 51 * 55 52 * The above file can also be read to know the currently loaded image. 56 53 * ··· 72 69 * to migrate those applications to other cores before running a core test. 73 70 * It may also be necessary to redirect interrupts to other CPUs. 74 71 * 75 - * In all cases reading the SCAN_STATUS MSR provides details on what 72 + * In all cases reading the corresponding test's STATUS MSR provides details on what 76 73 * happened. The driver makes the value of this MSR visible to applications 77 74 * via the "details" file (see below). Interrupted tests may be restarted. 78 75 * 79 - * The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_0/ 76 + * The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_<n>/ 80 77 * to control execution: 81 78 * 82 79 * Test a specific core:: 83 80 * 84 - * # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_0/run_test 81 + * # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_<n>/run_test 85 82 * 86 83 * when HT is enabled any of the sibling cpu# can be specified to test 87 84 * its corresponding physical core. Since the tests are per physical core, ··· 90 87 * 91 88 * For e.g. to test core corresponding to cpu5 92 89 * 93 - * # echo 5 > /sys/devices/virtual/misc/intel_ifs_0/run_test 90 + * # echo 5 > /sys/devices/virtual/misc/intel_ifs_<n>/run_test 94 91 * 95 92 * Results of the last test is provided in /sys:: 96 93 * 97 - * $ cat /sys/devices/virtual/misc/intel_ifs_0/status 94 + * $ cat /sys/devices/virtual/misc/intel_ifs_<n>/status 98 95 * pass 99 96 * 100 97 * Status can be one of pass, fail, untested 101 98 * 102 99 * Additional details of the last test is provided by the details file:: 103 100 * 104 - * $ cat /sys/devices/virtual/misc/intel_ifs_0/details 101 + * $ cat /sys/devices/virtual/misc/intel_ifs_<n>/details 105 102 * 0x8081 106 103 * 107 - * The details file reports the hex value of the SCAN_STATUS MSR. 104 + * The details file reports the hex value of the test specific status MSR. 108 105 * Hardware defined error codes are documented in volume 4 of the Intel 109 106 * Software Developer's Manual but the error_code field may contain one of 110 107 * the following driver defined software codes: ··· 130 127 #include <linux/device.h> 131 128 #include <linux/miscdevice.h> 132 129 130 + #define MSR_ARRAY_BIST 0x00000105 133 131 #define MSR_COPY_SCAN_HASHES 0x000002c2 134 132 #define MSR_SCAN_HASHES_STATUS 0x000002c3 135 133 #define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4 ··· 140 136 #define SCAN_NOT_TESTED 0 141 137 #define SCAN_TEST_PASS 1 142 138 #define SCAN_TEST_FAIL 2 139 + 140 + #define IFS_TYPE_SAF 0 141 + #define IFS_TYPE_ARRAY_BIST 1 143 142 144 143 /* MSR_SCAN_HASHES_STATUS bit fields */ 145 144 union ifs_scan_hashes_status { ··· 196 189 }; 197 190 }; 198 191 192 + /* MSR_ARRAY_BIST bit fields */ 193 + union ifs_array { 194 + u64 data; 195 + struct { 196 + u32 array_bitmask; 197 + u16 array_bank; 198 + u16 rsvd :15; 199 + u16 ctrl_result :1; 200 + }; 201 + }; 202 + 199 203 /* 200 204 * Driver populated error-codes 201 205 * 0xFD: Test timed out before completing all the chunks. ··· 215 197 #define IFS_SW_TIMEOUT 0xFD 216 198 #define IFS_SW_PARTIAL_COMPLETION 0xFE 217 199 200 + struct ifs_test_caps { 201 + int integrity_cap_bit; 202 + int test_num; 203 + }; 204 + 218 205 /** 219 206 * struct ifs_data - attributes related to intel IFS driver 220 - * @integrity_cap_bit: MSR_INTEGRITY_CAPS bit enumerating this test 221 207 * @loaded_version: stores the currently loaded ifs image version. 222 - * @pkg_auth: array of bool storing per package auth status 223 208 * @loaded: If a valid test binary has been loaded into the memory 224 209 * @loading_error: Error occurred on another CPU while loading image 225 210 * @valid_chunks: number of chunks which could be validated. 226 211 * @status: it holds simple status pass/fail/untested 227 212 * @scan_details: opaque scan status code from h/w 228 213 * @cur_batch: number indicating the currently loaded test file 229 - * @test_num: number indicating the test type 230 214 */ 231 215 struct ifs_data { 232 - int integrity_cap_bit; 233 - bool *pkg_auth; 234 216 int loaded_version; 235 217 bool loaded; 236 218 bool loading_error; ··· 238 220 int status; 239 221 u64 scan_details; 240 222 u32 cur_batch; 241 - int test_num; 242 223 }; 243 224 244 225 struct ifs_work { ··· 246 229 }; 247 230 248 231 struct ifs_device { 249 - struct ifs_data data; 232 + const struct ifs_test_caps *test_caps; 233 + struct ifs_data rw_data; 250 234 struct miscdevice misc; 251 235 }; 252 236 ··· 256 238 struct miscdevice *m = dev_get_drvdata(dev); 257 239 struct ifs_device *d = container_of(m, struct ifs_device, misc); 258 240 259 - return &d->data; 241 + return &d->rw_data; 260 242 } 261 243 244 + static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev) 245 + { 246 + struct miscdevice *m = dev_get_drvdata(dev); 247 + struct ifs_device *d = container_of(m, struct ifs_device, misc); 248 + 249 + return d->test_caps; 250 + } 251 + 252 + extern bool *ifs_pkg_auth; 262 253 int ifs_load_firmware(struct device *dev); 263 254 int do_core_test(int cpu, struct device *dev); 264 - const struct attribute_group **ifs_get_groups(void); 255 + extern struct attribute *plat_ifs_attrs[]; 256 + extern struct attribute *plat_ifs_array_attrs[]; 265 257 266 258 #endif
+5 -4
drivers/platform/x86/intel/ifs/load.c
··· 192 192 struct ifs_work local_work; 193 193 int curr_pkg, cpu, ret; 194 194 195 - memset(ifsd->pkg_auth, 0, (topology_max_packages() * sizeof(bool))); 195 + memset(ifs_pkg_auth, 0, (topology_max_packages() * sizeof(bool))); 196 196 ret = validate_ifs_metadata(dev); 197 197 if (ret) 198 198 return ret; ··· 204 204 cpus_read_lock(); 205 205 for_each_online_cpu(cpu) { 206 206 curr_pkg = topology_physical_package_id(cpu); 207 - if (ifsd->pkg_auth[curr_pkg]) 207 + if (ifs_pkg_auth[curr_pkg]) 208 208 continue; 209 209 reinit_completion(&ifs_done); 210 210 local_work.dev = dev; ··· 215 215 ret = -EIO; 216 216 goto out; 217 217 } 218 - ifsd->pkg_auth[curr_pkg] = 1; 218 + ifs_pkg_auth[curr_pkg] = 1; 219 219 } 220 220 ret = 0; 221 221 out: ··· 257 257 */ 258 258 int ifs_load_firmware(struct device *dev) 259 259 { 260 + const struct ifs_test_caps *test = ifs_get_test_caps(dev); 260 261 struct ifs_data *ifsd = ifs_get_data(dev); 261 262 const struct firmware *fw; 262 263 char scan_path[64]; 263 264 int ret = -EINVAL; 264 265 265 266 snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", 266 - ifsd->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, 267 + test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, 267 268 boot_cpu_data.x86_stepping, ifsd->cur_batch); 268 269 269 270 ret = request_firmware_direct(&fw, scan_path, dev);
+93 -1
drivers/platform/x86/intel/ifs/runtest.c
··· 229 229 } 230 230 } 231 231 232 + #define SPINUNIT 100 /* 100 nsec */ 233 + static atomic_t array_cpus_out; 234 + 235 + /* 236 + * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() 237 + */ 238 + static void wait_for_sibling_cpu(atomic_t *t, long long timeout) 239 + { 240 + int cpu = smp_processor_id(); 241 + const struct cpumask *smt_mask = cpu_smt_mask(cpu); 242 + int all_cpus = cpumask_weight(smt_mask); 243 + 244 + atomic_inc(t); 245 + while (atomic_read(t) < all_cpus) { 246 + if (timeout < SPINUNIT) 247 + return; 248 + ndelay(SPINUNIT); 249 + timeout -= SPINUNIT; 250 + touch_nmi_watchdog(); 251 + } 252 + } 253 + 254 + static int do_array_test(void *data) 255 + { 256 + union ifs_array *command = data; 257 + int cpu = smp_processor_id(); 258 + int first; 259 + 260 + /* 261 + * Only one logical CPU on a core needs to trigger the Array test via MSR write. 262 + */ 263 + first = cpumask_first(cpu_smt_mask(cpu)); 264 + 265 + if (cpu == first) { 266 + wrmsrl(MSR_ARRAY_BIST, command->data); 267 + /* Pass back the result of the test */ 268 + rdmsrl(MSR_ARRAY_BIST, command->data); 269 + } 270 + 271 + /* Tests complete faster if the sibling is spinning here */ 272 + wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC); 273 + 274 + return 0; 275 + } 276 + 277 + static void ifs_array_test_core(int cpu, struct device *dev) 278 + { 279 + union ifs_array command = {}; 280 + bool timed_out = false; 281 + struct ifs_data *ifsd; 282 + unsigned long timeout; 283 + 284 + ifsd = ifs_get_data(dev); 285 + 286 + command.array_bitmask = ~0U; 287 + timeout = jiffies + HZ / 2; 288 + 289 + do { 290 + if (time_after(jiffies, timeout)) { 291 + timed_out = true; 292 + break; 293 + } 294 + atomic_set(&array_cpus_out, 0); 295 + stop_core_cpuslocked(cpu, do_array_test, &command); 296 + 297 + if (command.ctrl_result) 298 + break; 299 + } while (command.array_bitmask); 300 + 301 + ifsd->scan_details = command.data; 302 + 303 + if (command.ctrl_result) 304 + ifsd->status = SCAN_TEST_FAIL; 305 + else if (timed_out || command.array_bitmask) 306 + ifsd->status = SCAN_NOT_TESTED; 307 + else 308 + ifsd->status = SCAN_TEST_PASS; 309 + } 310 + 232 311 /* 233 312 * Initiate per core test. It wakes up work queue threads on the target cpu and 234 313 * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and ··· 315 236 */ 316 237 int do_core_test(int cpu, struct device *dev) 317 238 { 239 + const struct ifs_test_caps *test = ifs_get_test_caps(dev); 240 + struct ifs_data *ifsd = ifs_get_data(dev); 318 241 int ret = 0; 319 242 320 243 /* Prevent CPUs from being taken offline during the scan test */ ··· 328 247 goto out; 329 248 } 330 249 331 - ifs_test_core(cpu, dev); 250 + switch (test->test_num) { 251 + case IFS_TYPE_SAF: 252 + if (!ifsd->loaded) 253 + return -EPERM; 254 + ifs_test_core(cpu, dev); 255 + break; 256 + case IFS_TYPE_ARRAY_BIST: 257 + ifs_array_test_core(cpu, dev); 258 + break; 259 + default: 260 + return -EINVAL; 261 + } 332 262 out: 333 263 cpus_read_unlock(); 334 264 return ret;
+9 -12
drivers/platform/x86/intel/ifs/sysfs.c
··· 64 64 struct device_attribute *attr, 65 65 const char *buf, size_t count) 66 66 { 67 - struct ifs_data *ifsd = ifs_get_data(dev); 68 67 unsigned int cpu; 69 68 int rc; 70 69 ··· 74 75 if (down_interruptible(&ifs_sem)) 75 76 return -EINTR; 76 77 77 - if (!ifsd->loaded) 78 - rc = -EPERM; 79 - else 80 - rc = do_core_test(cpu, dev); 78 + rc = do_core_test(cpu, dev); 81 79 82 80 up(&ifs_sem); 83 81 ··· 137 141 static DEVICE_ATTR_RO(image_version); 138 142 139 143 /* global scan sysfs attributes */ 140 - static struct attribute *plat_ifs_attrs[] = { 144 + struct attribute *plat_ifs_attrs[] = { 141 145 &dev_attr_details.attr, 142 146 &dev_attr_status.attr, 143 147 &dev_attr_run_test.attr, ··· 146 150 NULL 147 151 }; 148 152 149 - ATTRIBUTE_GROUPS(plat_ifs); 150 - 151 - const struct attribute_group **ifs_get_groups(void) 152 - { 153 - return plat_ifs_groups; 154 - } 153 + /* global array sysfs attributes */ 154 + struct attribute *plat_ifs_array_attrs[] = { 155 + &dev_attr_details.attr, 156 + &dev_attr_status.attr, 157 + &dev_attr_run_test.attr, 158 + NULL 159 + };
+2 -3
drivers/platform/x86/intel/int0002_vgpio.c
··· 223 223 return 0; 224 224 } 225 225 226 - static int int0002_remove(struct platform_device *pdev) 226 + static void int0002_remove(struct platform_device *pdev) 227 227 { 228 228 device_init_wakeup(&pdev->dev, false); 229 229 acpi_unregister_wakeup_handler(int0002_check_wake, NULL); 230 - return 0; 231 230 } 232 231 233 232 static int int0002_suspend(struct device *dev) ··· 272 273 .pm = &int0002_pm_ops, 273 274 }, 274 275 .probe = int0002_probe, 275 - .remove = int0002_remove, 276 + .remove_new = int0002_remove, 276 277 }; 277 278 278 279 module_platform_driver(int0002_driver);
+2 -3
drivers/platform/x86/intel/int1092/intel_sar.c
··· 292 292 return result; 293 293 } 294 294 295 - static int sar_remove(struct platform_device *device) 295 + static void sar_remove(struct platform_device *device) 296 296 { 297 297 struct wwan_sar_context *context = dev_get_drvdata(&device->dev); 298 298 int reg; ··· 304 304 kfree(context->config_data[reg].device_mode_info); 305 305 306 306 kfree(context); 307 - return 0; 308 307 } 309 308 310 309 static struct platform_driver sar_driver = { 311 310 .probe = sar_probe, 312 - .remove = sar_remove, 311 + .remove_new = sar_remove, 313 312 .driver = { 314 313 .name = DRVNAME, 315 314 .acpi_match_table = ACPI_PTR(sar_device_ids)
+2 -4
drivers/platform/x86/intel/int3472/discrete.c
··· 317 317 return 0; 318 318 } 319 319 320 - static int skl_int3472_discrete_remove(struct platform_device *pdev) 320 + static void skl_int3472_discrete_remove(struct platform_device *pdev) 321 321 { 322 322 struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); 323 323 ··· 326 326 skl_int3472_unregister_clock(int3472); 327 327 skl_int3472_unregister_pled(int3472); 328 328 skl_int3472_unregister_regulator(int3472); 329 - 330 - return 0; 331 329 } 332 330 333 331 static int skl_int3472_discrete_probe(struct platform_device *pdev) ··· 390 392 .acpi_match_table = int3472_device_id, 391 393 }, 392 394 .probe = skl_int3472_discrete_probe, 393 - .remove = skl_int3472_discrete_remove, 395 + .remove_new = skl_int3472_discrete_remove, 394 396 }; 395 397 module_platform_driver(int3472_discrete); 396 398
+2 -3
drivers/platform/x86/intel/mrfld_pwrbtn.c
··· 78 78 return 0; 79 79 } 80 80 81 - static int mrfld_pwrbtn_remove(struct platform_device *pdev) 81 + static void mrfld_pwrbtn_remove(struct platform_device *pdev) 82 82 { 83 83 struct device *dev = &pdev->dev; 84 84 85 85 dev_pm_clear_wake_irq(dev); 86 86 device_init_wakeup(dev, false); 87 - return 0; 88 87 } 89 88 90 89 static const struct platform_device_id mrfld_pwrbtn_id_table[] = { ··· 97 98 .name = "mrfld_bcove_pwrbtn", 98 99 }, 99 100 .probe = mrfld_pwrbtn_probe, 100 - .remove = mrfld_pwrbtn_remove, 101 + .remove_new = mrfld_pwrbtn_remove, 101 102 .id_table = mrfld_pwrbtn_id_table, 102 103 }; 103 104 module_platform_driver(mrfld_pwrbtn_driver);
+2 -3
drivers/platform/x86/intel/pmc/core.c
··· 1160 1160 return 0; 1161 1161 } 1162 1162 1163 - static int pmc_core_remove(struct platform_device *pdev) 1163 + static void pmc_core_remove(struct platform_device *pdev) 1164 1164 { 1165 1165 struct pmc_dev *pmcdev = platform_get_drvdata(pdev); 1166 1166 ··· 1168 1168 platform_set_drvdata(pdev, NULL); 1169 1169 mutex_destroy(&pmcdev->lock); 1170 1170 iounmap(pmcdev->regbase); 1171 - return 0; 1172 1171 } 1173 1172 1174 1173 static bool warn_on_s0ix_failures; ··· 1274 1275 .dev_groups = pmc_dev_groups, 1275 1276 }, 1276 1277 .probe = pmc_core_probe, 1277 - .remove = pmc_core_remove, 1278 + .remove_new = pmc_core_remove, 1278 1279 }; 1279 1280 1280 1281 module_platform_driver(pmc_core_driver);
+31
drivers/platform/x86/intel/pmc/mtl.c
··· 8 8 * 9 9 */ 10 10 11 + #include <linux/pci.h> 11 12 #include "core.h" 12 13 13 14 const struct pmc_reg_map mtl_reg_map = { ··· 46 45 pmc_core_send_ltr_ignore(pmcdev, 3); 47 46 } 48 47 48 + #define MTL_GNA_PCI_DEV 0x7e4c 49 + #define MTL_IPU_PCI_DEV 0x7d19 50 + #define MTL_VPU_PCI_DEV 0x7d1d 51 + static void mtl_set_device_d3(unsigned int device) 52 + { 53 + struct pci_dev *pcidev; 54 + 55 + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL); 56 + if (pcidev) { 57 + if (!device_trylock(&pcidev->dev)) { 58 + pci_dev_put(pcidev); 59 + return; 60 + } 61 + if (!pcidev->dev.driver) { 62 + dev_info(&pcidev->dev, "Setting to D3hot\n"); 63 + pci_set_power_state(pcidev, PCI_D3hot); 64 + } 65 + device_unlock(&pcidev->dev); 66 + pci_dev_put(pcidev); 67 + } 68 + } 69 + 49 70 void mtl_core_init(struct pmc_dev *pmcdev) 50 71 { 51 72 pmcdev->map = &mtl_reg_map; 52 73 pmcdev->core_configure = mtl_core_configure; 74 + 75 + /* 76 + * Set power state of select devices that do not have drivers to D3 77 + * so that they do not block Package C entry. 78 + */ 79 + mtl_set_device_d3(MTL_GNA_PCI_DEV); 80 + mtl_set_device_d3(MTL_IPU_PCI_DEV); 81 + mtl_set_device_d3(MTL_VPU_PCI_DEV); 53 82 }
+3 -3
drivers/platform/x86/intel/pmt/class.c
··· 33 33 */ 34 34 return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW); 35 35 } 36 - EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); 36 + EXPORT_SYMBOL_NS_GPL(intel_pmt_is_early_client_hw, INTEL_PMT); 37 37 38 38 static inline int 39 39 pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) ··· 327 327 return intel_pmt_dev_register(entry, ns, dev); 328 328 329 329 } 330 - EXPORT_SYMBOL_GPL(intel_pmt_dev_create); 330 + EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_create, INTEL_PMT); 331 331 332 332 void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, 333 333 struct intel_pmt_namespace *ns) ··· 343 343 device_unregister(dev); 344 344 xa_erase(ns->xa, entry->devid); 345 345 } 346 - EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy); 346 + EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_destroy, INTEL_PMT); 347 347 348 348 static int __init pmt_class_init(void) 349 349 {
+1
drivers/platform/x86/intel/pmt/crashlog.c
··· 328 328 MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); 329 329 MODULE_DESCRIPTION("Intel PMT Crashlog driver"); 330 330 MODULE_LICENSE("GPL v2"); 331 + MODULE_IMPORT_NS(INTEL_PMT);
+2 -1
drivers/platform/x86/intel/pmt/telemetry.c
··· 78 78 * reserved for future use. They have zero size. Do not fail 79 79 * probe for these. Just ignore them. 80 80 */ 81 - if (header->size == 0) 81 + if (header->size == 0 || header->access_type == 0xF) 82 82 return 1; 83 83 84 84 return 0; ··· 160 160 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 161 161 MODULE_DESCRIPTION("Intel PMT Telemetry driver"); 162 162 MODULE_LICENSE("GPL v2"); 163 + MODULE_IMPORT_NS(INTEL_PMT);
+1 -1
drivers/platform/x86/intel/sdsi.c
··· 49 49 #define SDSI_MBOX_CMD_SUCCESS 0x40 50 50 #define SDSI_MBOX_CMD_TIMEOUT 0x80 51 51 52 - #define MBOX_TIMEOUT_US 2000 52 + #define MBOX_TIMEOUT_US 500000 53 53 #define MBOX_TIMEOUT_ACQUIRE_US 1000 54 54 #define MBOX_POLLING_PERIOD_US 100 55 55 #define MBOX_ACQUIRE_NUM_RETRIES 5
+4
drivers/platform/x86/intel/speed_select_if/Kconfig
··· 2 2 depends on PCI 3 3 depends on X86_64 || COMPILE_TEST 4 4 5 + config INTEL_SPEED_SELECT_TPMI 6 + tristate 7 + 5 8 config INTEL_SPEED_SELECT_INTERFACE 6 9 tristate "Intel(R) Speed Select Technology interface drivers" 10 + select INTEL_SPEED_SELECT_TPMI if INTEL_TPMI 7 11 help 8 12 This config enables the Intel(R) Speed Select Technology interface 9 13 drivers. The Intel(R) speed select technology features are non
+2
drivers/platform/x86/intel/speed_select_if/Makefile
··· 8 8 obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o 9 9 obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o 10 10 obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o 11 + obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi_core.o 12 + obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi.o
+46 -1
drivers/platform/x86/intel/speed_select_if/isst_if_common.c
··· 19 19 #include <linux/uaccess.h> 20 20 #include <uapi/linux/isst_if.h> 21 21 22 + #include <asm/cpu_device_id.h> 23 + #include <asm/intel-family.h> 24 + 22 25 #include "isst_if_common.h" 23 26 24 27 #define MSR_THREAD_ID_INFO 0x53 28 + #define MSR_PM_LOGICAL_ID 0x54 25 29 #define MSR_CPU_BUS_NUMBER 0x128 26 30 27 31 static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX]; ··· 35 31 MSR_CONFIG_TDP_CONTROL, 36 32 MSR_TURBO_RATIO_LIMIT1, 37 33 MSR_TURBO_RATIO_LIMIT2, 34 + MSR_PM_LOGICAL_ID, 38 35 }; 39 36 40 37 struct isst_valid_cmd_ranges { ··· 77 72 int mbox_cmd_type; 78 73 u32 param; 79 74 }; 75 + 76 + static bool isst_hpm_support; 80 77 81 78 static DECLARE_HASHTABLE(isst_hash, 8); 82 79 static DEFINE_MUTEX(isst_hash_lock); ··· 269 262 } 270 263 EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req); 271 264 265 + static int isst_if_api_version; 266 + 272 267 static int isst_if_get_platform_info(void __user *argp) 273 268 { 274 269 struct isst_if_platform_info info; 275 270 276 - info.api_version = ISST_IF_API_VERSION; 271 + info.api_version = isst_if_api_version; 277 272 info.driver_version = ISST_IF_DRIVER_VERSION; 278 273 info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT; 279 274 info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered; ··· 418 409 isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1); 419 410 } 420 411 412 + if (isst_hpm_support) { 413 + 414 + ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); 415 + if (!ret) 416 + goto set_punit_id; 417 + } 418 + 421 419 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data); 422 420 if (ret) { 423 421 isst_cpu_info[cpu].punit_cpu_id = -1; 424 422 return ret; 425 423 } 424 + 425 + set_punit_id: 426 426 isst_cpu_info[cpu].punit_cpu_id = data; 427 427 428 428 isst_restore_msr_local(cpu); ··· 606 588 struct isst_if_cmd_cb cmd_cb; 607 589 struct isst_if_cmd_cb *cb; 608 590 long ret = -ENOTTY; 591 + int i; 609 592 610 593 switch (cmd) { 611 594 case ISST_IF_GET_PLATFORM_INFO: ··· 635 616 ret = isst_if_exec_multi_cmd(argp, &cmd_cb); 636 617 break; 637 618 default: 619 + for (i = 0; i < ISST_IF_DEV_MAX; ++i) { 620 + struct isst_if_cmd_cb *cb = &punit_callbacks[i]; 621 + int ret; 622 + 623 + if (cb->def_ioctl) { 624 + ret = cb->def_ioctl(file, cmd, arg); 625 + if (!ret) 626 + return ret; 627 + } 628 + } 638 629 break; 639 630 } 640 631 ··· 720 691 .fops = &isst_if_char_driver_ops, 721 692 }; 722 693 694 + static const struct x86_cpu_id hpm_cpu_ids[] = { 695 + X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL), 696 + X86_MATCH_INTEL_FAM6_MODEL(SIERRAFOREST_X, NULL), 697 + {} 698 + }; 699 + 723 700 static int isst_misc_reg(void) 724 701 { 725 702 mutex_lock(&punit_misc_dev_reg_lock); ··· 733 698 goto unlock_exit; 734 699 735 700 if (!misc_usage_count) { 701 + const struct x86_cpu_id *id; 702 + 703 + id = x86_match_cpu(hpm_cpu_ids); 704 + if (id) 705 + isst_hpm_support = true; 706 + 736 707 misc_device_ret = isst_if_cpu_info_init(); 737 708 if (misc_device_ret) 738 709 goto unlock_exit; ··· 797 756 mutex_unlock(&punit_misc_dev_open_lock); 798 757 return -EAGAIN; 799 758 } 759 + if (!cb->api_version) 760 + cb->api_version = ISST_IF_API_VERSION; 761 + if (cb->api_version > isst_if_api_version) 762 + isst_if_api_version = cb->api_version; 800 763 memcpy(&punit_callbacks[device_type], cb, sizeof(*cb)); 801 764 punit_callbacks[device_type].registered = 1; 802 765 mutex_unlock(&punit_misc_dev_open_lock);
+7 -1
drivers/platform/x86/intel/speed_select_if/isst_if_common.h
··· 30 30 31 31 #define ISST_IF_DEV_MBOX 0 32 32 #define ISST_IF_DEV_MMIO 1 33 - #define ISST_IF_DEV_MAX 2 33 + #define ISST_IF_DEV_TPMI 2 34 + #define ISST_IF_DEV_MAX 3 34 35 35 36 /** 36 37 * struct isst_if_cmd_cb - Used to register a IOCTL handler ··· 41 40 * @offset: Offset to the first valid member in command structure. 42 41 * This will be the offset of the start of the command 43 42 * after command count field 43 + * @api_version: API version supported for this target. 0, if none. 44 44 * @owner: Registered module owner 45 45 * @cmd_callback: Callback function to handle IOCTL. The callback has the 46 46 * command pointer with data for command. There is a pointer ··· 49 47 * response to user ioctl buffer. The "resume" argument 50 48 * can be used to avoid storing the command for replay 51 49 * during system resume 50 + * @def_ioctl: Default IOCTL handler callback, if there is no match in 51 + * the existing list of IOCTL handled by the common handler. 52 52 * 53 53 * This structure is used to register an handler for IOCTL. To avoid 54 54 * code duplication common code handles all the IOCTL command read/write ··· 61 57 int registered; 62 58 int cmd_size; 63 59 int offset; 60 + int api_version; 64 61 struct module *owner; 65 62 long (*cmd_callback)(u8 *ptr, int *write_only, int resume); 63 + long (*def_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); 66 64 }; 67 65 68 66 /* Internal interface functions */
+72
drivers/platform/x86/intel/speed_select_if/isst_tpmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * isst_tpmi.c: SST TPMI interface 4 + * 5 + * Copyright (c) 2023, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + */ 9 + 10 + #include <linux/auxiliary_bus.h> 11 + #include <linux/module.h> 12 + #include <linux/intel_tpmi.h> 13 + 14 + #include "isst_tpmi_core.h" 15 + 16 + static int intel_sst_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 17 + { 18 + int ret; 19 + 20 + ret = tpmi_sst_init(); 21 + if (ret) 22 + return ret; 23 + 24 + ret = tpmi_sst_dev_add(auxdev); 25 + if (ret) 26 + tpmi_sst_exit(); 27 + 28 + return ret; 29 + } 30 + 31 + static void intel_sst_remove(struct auxiliary_device *auxdev) 32 + { 33 + tpmi_sst_dev_remove(auxdev); 34 + tpmi_sst_exit(); 35 + } 36 + 37 + static int intel_sst_suspend(struct device *dev) 38 + { 39 + tpmi_sst_dev_suspend(to_auxiliary_dev(dev)); 40 + 41 + return 0; 42 + } 43 + 44 + static int intel_sst_resume(struct device *dev) 45 + { 46 + tpmi_sst_dev_resume(to_auxiliary_dev(dev)); 47 + 48 + return 0; 49 + } 50 + 51 + static DEFINE_SIMPLE_DEV_PM_OPS(intel_sst_pm, intel_sst_suspend, intel_sst_resume); 52 + 53 + static const struct auxiliary_device_id intel_sst_id_table[] = { 54 + { .name = "intel_vsec.tpmi-sst" }, 55 + {} 56 + }; 57 + MODULE_DEVICE_TABLE(auxiliary, intel_sst_id_table); 58 + 59 + static struct auxiliary_driver intel_sst_aux_driver = { 60 + .id_table = intel_sst_id_table, 61 + .remove = intel_sst_remove, 62 + .probe = intel_sst_probe, 63 + .driver = { 64 + .pm = pm_sleep_ptr(&intel_sst_pm), 65 + }, 66 + }; 67 + 68 + module_auxiliary_driver(intel_sst_aux_driver); 69 + 70 + MODULE_IMPORT_NS(INTEL_TPMI_SST); 71 + MODULE_DESCRIPTION("Intel TPMI SST Driver"); 72 + MODULE_LICENSE("GPL");
+1440
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * isst_tpmi.c: SST TPMI interface core 4 + * 5 + * Copyright (c) 2023, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + * This information will be useful to understand flows: 9 + * In the current generation of platforms, TPMI is supported via OOB 10 + * PCI device. This PCI device has one instance per CPU package. 11 + * There is a unique TPMI ID for SST. Each TPMI ID also has multiple 12 + * entries, representing per power domain information. 13 + * 14 + * There is one dev file for complete SST information and control same as the 15 + * prior generation of hardware. User spaces don't need to know how the 16 + * information is presented by the hardware. The TPMI core module implements 17 + * the hardware mapping. 18 + */ 19 + 20 + #include <linux/auxiliary_bus.h> 21 + #include <linux/delay.h> 22 + #include <linux/intel_tpmi.h> 23 + #include <linux/fs.h> 24 + #include <linux/io.h> 25 + #include <linux/kernel.h> 26 + #include <linux/module.h> 27 + #include <uapi/linux/isst_if.h> 28 + 29 + #include "isst_tpmi_core.h" 30 + #include "isst_if_common.h" 31 + 32 + /* Supported SST hardware version by this driver */ 33 + #define ISST_HEADER_VERSION 1 34 + 35 + /* 36 + * Used to indicate if value read from MMIO needs to get multiplied 37 + * to get to a standard unit or not. 38 + */ 39 + #define SST_MUL_FACTOR_NONE 1 40 + 41 + /* Define 100 as a scaling factor frequency ratio to frequency conversion */ 42 + #define SST_MUL_FACTOR_FREQ 100 43 + 44 + /* All SST regs are 64 bit size */ 45 + #define SST_REG_SIZE 8 46 + 47 + /** 48 + * struct sst_header - SST main header 49 + * @interface_version: Version number for this interface 50 + * @cap_mask: Bitmask of the supported sub features. 1=the sub feature is enabled. 51 + * 0=disabled. 52 + * Bit[8]= SST_CP enable (1), disable (0) 53 + * bit[9]= SST_PP enable (1), disable (0) 54 + * other bits are reserved for future use 55 + * @cp_offset: Qword (8 bytes) offset to the SST_CP register bank 56 + * @pp_offset: Qword (8 bytes) offset to the SST_PP register bank 57 + * @reserved: Reserved for future use 58 + * 59 + * This register allows SW to discover SST capability and the offsets to SST-CP 60 + * and SST-PP register banks. 61 + */ 62 + struct sst_header { 63 + u8 interface_version; 64 + u8 cap_mask; 65 + u8 cp_offset; 66 + u8 pp_offset; 67 + u32 reserved; 68 + } __packed; 69 + 70 + /** 71 + * struct cp_header - SST-CP (core-power) header 72 + * @feature_id: 0=SST-CP, 1=SST-PP, 2=SST-BF, 3=SST-TF 73 + * @feature_rev: Interface Version number for this SST feature 74 + * @ratio_unit: Frequency ratio unit. 00: 100MHz. All others are reserved 75 + * @reserved: Reserved for future use 76 + * 77 + * This structure is used store SST-CP header. This is packed to the same 78 + * format as defined in the specifications. 79 + */ 80 + struct cp_header { 81 + u64 feature_id :4; 82 + u64 feature_rev :8; 83 + u64 ratio_unit :2; 84 + u64 reserved :50; 85 + } __packed; 86 + 87 + /** 88 + * struct pp_header - SST-PP (Perf profile) header 89 + * @feature_id: 0=SST-CP, 1=SST-PP, 2=SST-BF, 3=SST-TF 90 + * @feature_rev: Interface Version number for this SST feature 91 + * @level_en_mask: SST-PP level enable/disable fuse mask 92 + * @allowed_level_mask: Allowed level mask used for dynamic config level switching 93 + * @reserved0: Reserved for future use 94 + * @ratio_unit: Frequency ratio unit. 00: 100MHz. All others are reserved 95 + * @block_size: Size of PP block in Qword unit (8 bytes) 96 + * @dynamic_switch: If set (1), dynamic switching of SST PP is supported 97 + * @memory_ratio_unit: Memory Controller frequency ratio unit. 00: 100MHz, others reserved 98 + * @reserved1: Reserved for future use 99 + * 100 + * This structure is used store SST-PP header. This is packed to the same 101 + * format as defined in the specifications. 102 + */ 103 + struct pp_header { 104 + u64 feature_id :4; 105 + u64 feature_rev :8; 106 + u64 level_en_mask :8; 107 + u64 allowed_level_mask :8; 108 + u64 reserved0 :4; 109 + u64 ratio_unit :2; 110 + u64 block_size :8; 111 + u64 dynamic_switch :1; 112 + u64 memory_ratio_unit :2; 113 + u64 reserved1 :19; 114 + } __packed; 115 + 116 + /** 117 + * struct feature_offset - Offsets to SST-PP features 118 + * @pp_offset: Qword offset within PP level for the SST_PP register bank 119 + * @bf_offset: Qword offset within PP level for the SST_BF register bank 120 + * @tf_offset: Qword offset within PP level for the SST_TF register bank 121 + * @reserved: Reserved for future use 122 + * 123 + * This structure is used store offsets for SST features in the register bank. 124 + * This is packed to the same format as defined in the specifications. 125 + */ 126 + struct feature_offset { 127 + u64 pp_offset :8; 128 + u64 bf_offset :8; 129 + u64 tf_offset :8; 130 + u64 reserved :40; 131 + } __packed; 132 + 133 + /** 134 + * struct levels_offset - Offsets to each SST PP level 135 + * @sst_pp_level0_offset: Qword offset to the register block of PP level 0 136 + * @sst_pp_level1_offset: Qword offset to the register block of PP level 1 137 + * @sst_pp_level2_offset: Qword offset to the register block of PP level 2 138 + * @sst_pp_level3_offset: Qword offset to the register block of PP level 3 139 + * @sst_pp_level4_offset: Qword offset to the register block of PP level 4 140 + * @reserved: Reserved for future use 141 + * 142 + * This structure is used store offsets of SST PP levels in the register bank. 143 + * This is packed to the same format as defined in the specifications. 144 + */ 145 + struct levels_offset { 146 + u64 sst_pp_level0_offset :8; 147 + u64 sst_pp_level1_offset :8; 148 + u64 sst_pp_level2_offset :8; 149 + u64 sst_pp_level3_offset :8; 150 + u64 sst_pp_level4_offset :8; 151 + u64 reserved :24; 152 + } __packed; 153 + 154 + /** 155 + * struct pp_control_offset - Offsets for SST PP controls 156 + * @perf_level: A SST-PP level that SW intends to switch to 157 + * @perf_level_lock: SST-PP level select lock. 0 - unlocked. 1 - locked till next reset 158 + * @resvd0: Reserved for future use 159 + * @current_state: Bit mask to control the enable(1)/disable(0) state of each feature 160 + * of the current PP level, bit 0 = BF, bit 1 = TF, bit 2-7 = reserved 161 + * @reserved: Reserved for future use 162 + * 163 + * This structure is used store offsets of SST PP controls in the register bank. 164 + * This is packed to the same format as defined in the specifications. 165 + */ 166 + struct pp_control_offset { 167 + u64 perf_level :3; 168 + u64 perf_level_lock :1; 169 + u64 resvd0 :4; 170 + u64 current_state :8; 171 + u64 reserved :48; 172 + } __packed; 173 + 174 + /** 175 + * struct pp_status_offset - Offsets for SST PP status fields 176 + * @sst_pp_level: Returns the current SST-PP level 177 + * @sst_pp_lock: Returns the lock bit setting of perf_level_lock in pp_control_offset 178 + * @error_type: Returns last error of SST-PP level change request. 0: no error, 179 + * 1: level change not allowed, others: reserved 180 + * @feature_state: Bit mask to indicate the enable(1)/disable(0) state of each feature of the 181 + * current PP level. bit 0 = BF, bit 1 = TF, bit 2-7 reserved 182 + * @reserved0: Reserved for future use 183 + * @feature_error_type: Returns last error of the specific feature. Three error_type bits per 184 + * feature. i.e. ERROR_TYPE[2:0] for BF, ERROR_TYPE[5:3] for TF, etc. 185 + * 0x0: no error, 0x1: The specific feature is not supported by the hardware. 186 + * 0x2-0x6: Reserved. 0x7: feature state change is not allowed. 187 + * @reserved1: Reserved for future use 188 + * 189 + * This structure is used store offsets of SST PP status in the register bank. 190 + * This is packed to the same format as defined in the specifications. 191 + */ 192 + struct pp_status_offset { 193 + u64 sst_pp_level :3; 194 + u64 sst_pp_lock :1; 195 + u64 error_type :4; 196 + u64 feature_state :8; 197 + u64 reserved0 :16; 198 + u64 feature_error_type : 24; 199 + u64 reserved1 :8; 200 + } __packed; 201 + 202 + /** 203 + * struct perf_level - Used to store perf level and mmio offset 204 + * @mmio_offset: mmio offset for a perf level 205 + * @level: perf level for this offset 206 + * 207 + * This structure is used store final mmio offset of each perf level from the 208 + * SST base mmio offset. 209 + */ 210 + struct perf_level { 211 + int mmio_offset; 212 + int level; 213 + }; 214 + 215 + /** 216 + * struct tpmi_per_power_domain_info - Store per power_domain SST info 217 + * @package_id: Package id for this power_domain 218 + * @power_domain_id: Power domain id, Each entry from the SST-TPMI instance is a power_domain. 219 + * @max_level: Max possible PP level possible for this power_domain 220 + * @ratio_unit: Ratio unit for converting to MHz 221 + * @avx_levels: Number of AVX levels 222 + * @pp_block_size: Block size from PP header 223 + * @sst_header: Store SST header for this power_domain 224 + * @cp_header: Store SST-CP header for this power_domain 225 + * @pp_header: Store SST-PP header for this power_domain 226 + * @perf_levels: Pointer to each perf level to map level to mmio offset 227 + * @feature_offsets: Store feature offsets for each PP-level 228 + * @control_offset: Store the control offset for each PP-level 229 + * @status_offset: Store the status offset for each PP-level 230 + * @sst_base: Mapped SST base IO memory 231 + * @auxdev: Auxiliary device instance enumerated this instance 232 + * @saved_sst_cp_control: Save SST-CP control configuration to store restore for suspend/resume 233 + * @saved_clos_configs: Save SST-CP CLOS configuration to store restore for suspend/resume 234 + * @saved_clos_assocs: Save SST-CP CLOS association to store restore for suspend/resume 235 + * @saved_pp_control: Save SST-PP control information to store restore for suspend/resume 236 + * 237 + * This structure is used store complete SST information for a power_domain. This information 238 + * is used to read/write request for any SST IOCTL. Each physical CPU package can have multiple 239 + * power_domains. Each power domain describes its own SST information and has its own controls. 240 + */ 241 + struct tpmi_per_power_domain_info { 242 + int package_id; 243 + int power_domain_id; 244 + int max_level; 245 + int ratio_unit; 246 + int avx_levels; 247 + int pp_block_size; 248 + struct sst_header sst_header; 249 + struct cp_header cp_header; 250 + struct pp_header pp_header; 251 + struct perf_level *perf_levels; 252 + struct feature_offset feature_offsets; 253 + struct pp_control_offset control_offset; 254 + struct pp_status_offset status_offset; 255 + void __iomem *sst_base; 256 + struct auxiliary_device *auxdev; 257 + u64 saved_sst_cp_control; 258 + u64 saved_clos_configs[4]; 259 + u64 saved_clos_assocs[4]; 260 + u64 saved_pp_control; 261 + }; 262 + 263 + /** 264 + * struct tpmi_sst_struct - Store sst info for a package 265 + * @package_id: Package id for this aux device instance 266 + * @number_of_power_domains: Number of power_domains pointed by power_domain_info pointer 267 + * @power_domain_info: Pointer to power domains information 268 + * 269 + * This structure is used store full SST information for a package. 270 + * Each package has a unique OOB PCI device, which enumerates TPMI. 271 + * Each Package will have multiple power_domains. 272 + */ 273 + struct tpmi_sst_struct { 274 + int package_id; 275 + int number_of_power_domains; 276 + struct tpmi_per_power_domain_info *power_domain_info; 277 + }; 278 + 279 + /** 280 + * struct tpmi_sst_common_struct - Store all SST instances 281 + * @max_index: Maximum instances currently present 282 + * @sst_inst: Pointer to per package instance 283 + * 284 + * Stores every SST Package instance. 285 + */ 286 + struct tpmi_sst_common_struct { 287 + int max_index; 288 + struct tpmi_sst_struct **sst_inst; 289 + }; 290 + 291 + /* 292 + * Each IOCTL request is processed under this lock. Also used to protect 293 + * registration functions and common data structures. 294 + */ 295 + static DEFINE_MUTEX(isst_tpmi_dev_lock); 296 + 297 + /* Usage count to track, number of TPMI SST instances registered to this core. */ 298 + static int isst_core_usage_count; 299 + 300 + /* Stores complete SST information for every package and power_domain */ 301 + static struct tpmi_sst_common_struct isst_common; 302 + 303 + #define SST_MAX_AVX_LEVELS 3 304 + 305 + #define SST_PP_OFFSET_0 8 306 + #define SST_PP_OFFSET_1 16 307 + #define SST_PP_OFFSET_SIZE 8 308 + 309 + static int sst_add_perf_profiles(struct auxiliary_device *auxdev, 310 + struct tpmi_per_power_domain_info *pd_info, 311 + int levels) 312 + { 313 + u64 perf_level_offsets; 314 + int i; 315 + 316 + pd_info->perf_levels = devm_kcalloc(&auxdev->dev, levels, 317 + sizeof(struct perf_level), 318 + GFP_KERNEL); 319 + if (!pd_info->perf_levels) 320 + return 0; 321 + 322 + pd_info->ratio_unit = pd_info->pp_header.ratio_unit; 323 + pd_info->avx_levels = SST_MAX_AVX_LEVELS; 324 + pd_info->pp_block_size = pd_info->pp_header.block_size; 325 + 326 + /* Read PP Offset 0: Get feature offset with PP level */ 327 + *((u64 *)&pd_info->feature_offsets) = readq(pd_info->sst_base + 328 + pd_info->sst_header.pp_offset + 329 + SST_PP_OFFSET_0); 330 + 331 + perf_level_offsets = readq(pd_info->sst_base + pd_info->sst_header.pp_offset + 332 + SST_PP_OFFSET_1); 333 + 334 + for (i = 0; i < levels; ++i) { 335 + u64 offset; 336 + 337 + offset = perf_level_offsets & (0xffULL << (i * SST_PP_OFFSET_SIZE)); 338 + offset >>= (i * 8); 339 + offset &= 0xff; 340 + offset *= 8; /* Convert to byte from QWORD offset */ 341 + pd_info->perf_levels[i].mmio_offset = pd_info->sst_header.pp_offset + offset; 342 + } 343 + 344 + return 0; 345 + } 346 + 347 + static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info) 348 + { 349 + int i, mask, levels; 350 + 351 + *((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base); 352 + pd_info->sst_header.cp_offset *= 8; 353 + pd_info->sst_header.pp_offset *= 8; 354 + 355 + if (pd_info->sst_header.interface_version != ISST_HEADER_VERSION) { 356 + dev_err(&auxdev->dev, "SST: Unsupported version:%x\n", 357 + pd_info->sst_header.interface_version); 358 + return -ENODEV; 359 + } 360 + 361 + /* Read SST CP Header */ 362 + *((u64 *)&pd_info->cp_header) = readq(pd_info->sst_base + pd_info->sst_header.cp_offset); 363 + 364 + /* Read PP header */ 365 + *((u64 *)&pd_info->pp_header) = readq(pd_info->sst_base + pd_info->sst_header.pp_offset); 366 + 367 + /* Force level_en_mask level 0 */ 368 + pd_info->pp_header.level_en_mask |= 0x01; 369 + 370 + mask = 0x01; 371 + levels = 0; 372 + for (i = 0; i < 8; ++i) { 373 + if (pd_info->pp_header.level_en_mask & mask) 374 + levels = i; 375 + mask <<= 1; 376 + } 377 + pd_info->max_level = levels; 378 + sst_add_perf_profiles(auxdev, pd_info, levels + 1); 379 + 380 + return 0; 381 + } 382 + 383 + /* 384 + * Map a package and power_domain id to SST information structure unique for a power_domain. 385 + * The caller should call under isst_tpmi_dev_lock. 386 + */ 387 + static struct tpmi_per_power_domain_info *get_instance(int pkg_id, int power_domain_id) 388 + { 389 + struct tpmi_per_power_domain_info *power_domain_info; 390 + struct tpmi_sst_struct *sst_inst; 391 + 392 + if (pkg_id < 0 || pkg_id > isst_common.max_index || 393 + pkg_id >= topology_max_packages()) 394 + return NULL; 395 + 396 + sst_inst = isst_common.sst_inst[pkg_id]; 397 + if (!sst_inst) 398 + return NULL; 399 + 400 + if (power_domain_id < 0 || power_domain_id >= sst_inst->number_of_power_domains) 401 + return NULL; 402 + 403 + power_domain_info = &sst_inst->power_domain_info[power_domain_id]; 404 + 405 + if (power_domain_info && !power_domain_info->sst_base) 406 + return NULL; 407 + 408 + return power_domain_info; 409 + } 410 + 411 + static bool disable_dynamic_sst_features(void) 412 + { 413 + u64 value; 414 + 415 + rdmsrl(MSR_PM_ENABLE, value); 416 + return !(value & 0x1); 417 + } 418 + 419 + #define _read_cp_info(name_str, name, offset, start, width, mult_factor)\ 420 + {\ 421 + u64 val, mask;\ 422 + \ 423 + val = readq(power_domain_info->sst_base + power_domain_info->sst_header.cp_offset +\ 424 + (offset));\ 425 + mask = GENMASK_ULL((start + width - 1), start);\ 426 + val &= mask; \ 427 + val >>= start;\ 428 + name = (val * mult_factor);\ 429 + } 430 + 431 + #define _write_cp_info(name_str, name, offset, start, width, div_factor)\ 432 + {\ 433 + u64 val, mask;\ 434 + \ 435 + val = readq(power_domain_info->sst_base +\ 436 + power_domain_info->sst_header.cp_offset + (offset));\ 437 + mask = GENMASK_ULL((start + width - 1), start);\ 438 + val &= ~mask;\ 439 + val |= (name / div_factor) << start;\ 440 + writeq(val, power_domain_info->sst_base + power_domain_info->sst_header.cp_offset +\ 441 + (offset));\ 442 + } 443 + 444 + #define SST_CP_CONTROL_OFFSET 8 445 + #define SST_CP_STATUS_OFFSET 16 446 + 447 + #define SST_CP_ENABLE_START 0 448 + #define SST_CP_ENABLE_WIDTH 1 449 + 450 + #define SST_CP_PRIORITY_TYPE_START 1 451 + #define SST_CP_PRIORITY_TYPE_WIDTH 1 452 + 453 + static long isst_if_core_power_state(void __user *argp) 454 + { 455 + struct tpmi_per_power_domain_info *power_domain_info; 456 + struct isst_core_power core_power; 457 + 458 + if (disable_dynamic_sst_features()) 459 + return -EFAULT; 460 + 461 + if (copy_from_user(&core_power, argp, sizeof(core_power))) 462 + return -EFAULT; 463 + 464 + power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id); 465 + if (!power_domain_info) 466 + return -EINVAL; 467 + 468 + if (core_power.get_set) { 469 + _write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET, 470 + SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE) 471 + _write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET, 472 + SST_CP_PRIORITY_TYPE_START, SST_CP_PRIORITY_TYPE_WIDTH, 473 + SST_MUL_FACTOR_NONE) 474 + } else { 475 + /* get */ 476 + _read_cp_info("cp_enable", core_power.enable, SST_CP_STATUS_OFFSET, 477 + SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE) 478 + _read_cp_info("cp_prio_type", core_power.priority_type, SST_CP_STATUS_OFFSET, 479 + SST_CP_PRIORITY_TYPE_START, SST_CP_PRIORITY_TYPE_WIDTH, 480 + SST_MUL_FACTOR_NONE) 481 + core_power.supported = !!(power_domain_info->sst_header.cap_mask & BIT(0)); 482 + if (copy_to_user(argp, &core_power, sizeof(core_power))) 483 + return -EFAULT; 484 + } 485 + 486 + return 0; 487 + } 488 + 489 + #define SST_CLOS_CONFIG_0_OFFSET 24 490 + 491 + #define SST_CLOS_CONFIG_PRIO_START 4 492 + #define SST_CLOS_CONFIG_PRIO_WIDTH 4 493 + 494 + #define SST_CLOS_CONFIG_MIN_START 8 495 + #define SST_CLOS_CONFIG_MIN_WIDTH 8 496 + 497 + #define SST_CLOS_CONFIG_MAX_START 16 498 + #define SST_CLOS_CONFIG_MAX_WIDTH 8 499 + 500 + static long isst_if_clos_param(void __user *argp) 501 + { 502 + struct tpmi_per_power_domain_info *power_domain_info; 503 + struct isst_clos_param clos_param; 504 + 505 + if (copy_from_user(&clos_param, argp, sizeof(clos_param))) 506 + return -EFAULT; 507 + 508 + power_domain_info = get_instance(clos_param.socket_id, clos_param.power_domain_id); 509 + if (!power_domain_info) 510 + return -EINVAL; 511 + 512 + if (clos_param.get_set) { 513 + _write_cp_info("clos.min_freq", clos_param.min_freq_mhz, 514 + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), 515 + SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH, 516 + SST_MUL_FACTOR_FREQ); 517 + _write_cp_info("clos.max_freq", clos_param.max_freq_mhz, 518 + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), 519 + SST_CLOS_CONFIG_MAX_START, SST_CLOS_CONFIG_MAX_WIDTH, 520 + SST_MUL_FACTOR_FREQ); 521 + _write_cp_info("clos.prio", clos_param.prop_prio, 522 + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), 523 + SST_CLOS_CONFIG_PRIO_START, SST_CLOS_CONFIG_PRIO_WIDTH, 524 + SST_MUL_FACTOR_NONE); 525 + } else { 526 + /* get */ 527 + _read_cp_info("clos.min_freq", clos_param.min_freq_mhz, 528 + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), 529 + SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH, 530 + SST_MUL_FACTOR_FREQ) 531 + _read_cp_info("clos.max_freq", clos_param.max_freq_mhz, 532 + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), 533 + SST_CLOS_CONFIG_MAX_START, SST_CLOS_CONFIG_MAX_WIDTH, 534 + SST_MUL_FACTOR_FREQ) 535 + _read_cp_info("clos.prio", clos_param.prop_prio, 536 + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), 537 + SST_CLOS_CONFIG_PRIO_START, SST_CLOS_CONFIG_PRIO_WIDTH, 538 + SST_MUL_FACTOR_NONE) 539 + 540 + if (copy_to_user(argp, &clos_param, sizeof(clos_param))) 541 + return -EFAULT; 542 + } 543 + 544 + return 0; 545 + } 546 + 547 + #define SST_CLOS_ASSOC_0_OFFSET 56 548 + #define SST_CLOS_ASSOC_CPUS_PER_REG 16 549 + #define SST_CLOS_ASSOC_BITS_PER_CPU 4 550 + 551 + static long isst_if_clos_assoc(void __user *argp) 552 + { 553 + struct isst_if_clos_assoc_cmds assoc_cmds; 554 + unsigned char __user *ptr; 555 + int i; 556 + 557 + /* Each multi command has u16 command count as the first field */ 558 + if (copy_from_user(&assoc_cmds, argp, sizeof(assoc_cmds))) 559 + return -EFAULT; 560 + 561 + if (!assoc_cmds.cmd_count || assoc_cmds.cmd_count > ISST_IF_CMD_LIMIT) 562 + return -EINVAL; 563 + 564 + ptr = argp + offsetof(struct isst_if_clos_assoc_cmds, assoc_info); 565 + for (i = 0; i < assoc_cmds.cmd_count; ++i) { 566 + struct tpmi_per_power_domain_info *power_domain_info; 567 + struct isst_if_clos_assoc clos_assoc; 568 + int punit_id, punit_cpu_no, pkg_id; 569 + struct tpmi_sst_struct *sst_inst; 570 + int offset, shift, cpu; 571 + u64 val, mask, clos; 572 + 573 + if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc))) 574 + return -EFAULT; 575 + 576 + if (clos_assoc.socket_id > topology_max_packages()) 577 + return -EINVAL; 578 + 579 + cpu = clos_assoc.logical_cpu; 580 + clos = clos_assoc.clos; 581 + 582 + if (assoc_cmds.punit_cpu_map) 583 + punit_cpu_no = cpu; 584 + else 585 + return -EOPNOTSUPP; 586 + 587 + if (punit_cpu_no < 0) 588 + return -EINVAL; 589 + 590 + punit_id = clos_assoc.power_domain_id; 591 + pkg_id = clos_assoc.socket_id; 592 + 593 + sst_inst = isst_common.sst_inst[pkg_id]; 594 + 595 + if (clos_assoc.power_domain_id > sst_inst->number_of_power_domains) 596 + return -EINVAL; 597 + 598 + power_domain_info = &sst_inst->power_domain_info[punit_id]; 599 + 600 + offset = SST_CLOS_ASSOC_0_OFFSET + 601 + (punit_cpu_no / SST_CLOS_ASSOC_CPUS_PER_REG) * SST_REG_SIZE; 602 + shift = punit_cpu_no % SST_CLOS_ASSOC_CPUS_PER_REG; 603 + shift *= SST_CLOS_ASSOC_BITS_PER_CPU; 604 + 605 + val = readq(power_domain_info->sst_base + 606 + power_domain_info->sst_header.cp_offset + offset); 607 + if (assoc_cmds.get_set) { 608 + mask = GENMASK_ULL((shift + SST_CLOS_ASSOC_BITS_PER_CPU - 1), shift); 609 + val &= ~mask; 610 + val |= (clos << shift); 611 + writeq(val, power_domain_info->sst_base + 612 + power_domain_info->sst_header.cp_offset + offset); 613 + } else { 614 + val >>= shift; 615 + clos_assoc.clos = val & GENMASK(SST_CLOS_ASSOC_BITS_PER_CPU - 1, 0); 616 + if (copy_to_user(ptr, &clos_assoc, sizeof(clos_assoc))) 617 + return -EFAULT; 618 + } 619 + 620 + ptr += sizeof(clos_assoc); 621 + } 622 + 623 + return 0; 624 + } 625 + 626 + #define _read_pp_info(name_str, name, offset, start, width, mult_factor)\ 627 + {\ 628 + u64 val, _mask;\ 629 + \ 630 + val = readq(power_domain_info->sst_base + power_domain_info->sst_header.pp_offset +\ 631 + (offset));\ 632 + _mask = GENMASK_ULL((start + width - 1), start);\ 633 + val &= _mask;\ 634 + val >>= start;\ 635 + name = (val * mult_factor);\ 636 + } 637 + 638 + #define _write_pp_info(name_str, name, offset, start, width, div_factor)\ 639 + {\ 640 + u64 val, _mask;\ 641 + \ 642 + val = readq(power_domain_info->sst_base + power_domain_info->sst_header.pp_offset +\ 643 + (offset));\ 644 + _mask = GENMASK((start + width - 1), start);\ 645 + val &= ~_mask;\ 646 + val |= (name / div_factor) << start;\ 647 + writeq(val, power_domain_info->sst_base + power_domain_info->sst_header.pp_offset +\ 648 + (offset));\ 649 + } 650 + 651 + #define _read_bf_level_info(name_str, name, level, offset, start, width, mult_factor)\ 652 + {\ 653 + u64 val, _mask;\ 654 + \ 655 + val = readq(power_domain_info->sst_base +\ 656 + power_domain_info->perf_levels[level].mmio_offset +\ 657 + (power_domain_info->feature_offsets.bf_offset * 8) + (offset));\ 658 + _mask = GENMASK_ULL((start + width - 1), start);\ 659 + val &= _mask; \ 660 + val >>= start;\ 661 + name = (val * mult_factor);\ 662 + } 663 + 664 + #define _read_tf_level_info(name_str, name, level, offset, start, width, mult_factor)\ 665 + {\ 666 + u64 val, _mask;\ 667 + \ 668 + val = readq(power_domain_info->sst_base +\ 669 + power_domain_info->perf_levels[level].mmio_offset +\ 670 + (power_domain_info->feature_offsets.tf_offset * 8) + (offset));\ 671 + _mask = GENMASK_ULL((start + width - 1), start);\ 672 + val &= _mask; \ 673 + val >>= start;\ 674 + name = (val * mult_factor);\ 675 + } 676 + 677 + #define SST_PP_STATUS_OFFSET 32 678 + 679 + #define SST_PP_LEVEL_START 0 680 + #define SST_PP_LEVEL_WIDTH 3 681 + 682 + #define SST_PP_LOCK_START 3 683 + #define SST_PP_LOCK_WIDTH 1 684 + 685 + #define SST_PP_FEATURE_STATE_START 8 686 + #define SST_PP_FEATURE_STATE_WIDTH 8 687 + 688 + #define SST_BF_FEATURE_SUPPORTED_START 12 689 + #define SST_BF_FEATURE_SUPPORTED_WIDTH 1 690 + 691 + #define SST_TF_FEATURE_SUPPORTED_START 12 692 + #define SST_TF_FEATURE_SUPPORTED_WIDTH 1 693 + 694 + static int isst_if_get_perf_level(void __user *argp) 695 + { 696 + struct isst_perf_level_info perf_level; 697 + struct tpmi_per_power_domain_info *power_domain_info; 698 + 699 + if (copy_from_user(&perf_level, argp, sizeof(perf_level))) 700 + return -EFAULT; 701 + 702 + power_domain_info = get_instance(perf_level.socket_id, perf_level.power_domain_id); 703 + if (!power_domain_info) 704 + return -EINVAL; 705 + 706 + perf_level.max_level = power_domain_info->max_level; 707 + perf_level.level_mask = power_domain_info->pp_header.allowed_level_mask; 708 + perf_level.feature_rev = power_domain_info->pp_header.feature_rev; 709 + _read_pp_info("current_level", perf_level.current_level, SST_PP_STATUS_OFFSET, 710 + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) 711 + _read_pp_info("locked", perf_level.locked, SST_PP_STATUS_OFFSET, 712 + SST_PP_LOCK_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) 713 + _read_pp_info("feature_state", perf_level.feature_state, SST_PP_STATUS_OFFSET, 714 + SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE) 715 + perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1)); 716 + 717 + _read_bf_level_info("bf_support", perf_level.sst_bf_support, 0, 0, 718 + SST_BF_FEATURE_SUPPORTED_START, SST_BF_FEATURE_SUPPORTED_WIDTH, 719 + SST_MUL_FACTOR_NONE); 720 + _read_tf_level_info("tf_support", perf_level.sst_tf_support, 0, 0, 721 + SST_TF_FEATURE_SUPPORTED_START, SST_TF_FEATURE_SUPPORTED_WIDTH, 722 + SST_MUL_FACTOR_NONE); 723 + 724 + if (copy_to_user(argp, &perf_level, sizeof(perf_level))) 725 + return -EFAULT; 726 + 727 + return 0; 728 + } 729 + 730 + #define SST_PP_CONTROL_OFFSET 24 731 + #define SST_PP_LEVEL_CHANGE_TIME_MS 5 732 + #define SST_PP_LEVEL_CHANGE_RETRY_COUNT 3 733 + 734 + static int isst_if_set_perf_level(void __user *argp) 735 + { 736 + struct isst_perf_level_control perf_level; 737 + struct tpmi_per_power_domain_info *power_domain_info; 738 + int level, retry = 0; 739 + 740 + if (disable_dynamic_sst_features()) 741 + return -EFAULT; 742 + 743 + if (copy_from_user(&perf_level, argp, sizeof(perf_level))) 744 + return -EFAULT; 745 + 746 + power_domain_info = get_instance(perf_level.socket_id, perf_level.power_domain_id); 747 + if (!power_domain_info) 748 + return -EINVAL; 749 + 750 + if (!(power_domain_info->pp_header.allowed_level_mask & BIT(perf_level.level))) 751 + return -EINVAL; 752 + 753 + _read_pp_info("current_level", level, SST_PP_STATUS_OFFSET, 754 + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) 755 + 756 + /* If the requested new level is same as the current level, reject */ 757 + if (perf_level.level == level) 758 + return -EINVAL; 759 + 760 + _write_pp_info("perf_level", perf_level.level, SST_PP_CONTROL_OFFSET, 761 + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) 762 + 763 + /* It is possible that firmware is busy (although unlikely), so retry */ 764 + do { 765 + /* Give time to FW to process */ 766 + msleep(SST_PP_LEVEL_CHANGE_TIME_MS); 767 + 768 + _read_pp_info("current_level", level, SST_PP_STATUS_OFFSET, 769 + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) 770 + 771 + /* Check if the new level is active */ 772 + if (perf_level.level == level) 773 + break; 774 + 775 + } while (retry++ < SST_PP_LEVEL_CHANGE_RETRY_COUNT); 776 + 777 + /* If the level change didn't happen, return fault */ 778 + if (perf_level.level != level) 779 + return -EFAULT; 780 + 781 + /* Reset the feature state on level change */ 782 + _write_pp_info("perf_feature", 0, SST_PP_CONTROL_OFFSET, 783 + SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, 784 + SST_MUL_FACTOR_NONE) 785 + 786 + /* Give time to FW to process */ 787 + msleep(SST_PP_LEVEL_CHANGE_TIME_MS); 788 + 789 + return 0; 790 + } 791 + 792 + static int isst_if_set_perf_feature(void __user *argp) 793 + { 794 + struct isst_perf_feature_control perf_feature; 795 + struct tpmi_per_power_domain_info *power_domain_info; 796 + 797 + if (disable_dynamic_sst_features()) 798 + return -EFAULT; 799 + 800 + if (copy_from_user(&perf_feature, argp, sizeof(perf_feature))) 801 + return -EFAULT; 802 + 803 + power_domain_info = get_instance(perf_feature.socket_id, perf_feature.power_domain_id); 804 + if (!power_domain_info) 805 + return -EINVAL; 806 + 807 + _write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET, 808 + SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, 809 + SST_MUL_FACTOR_NONE) 810 + 811 + return 0; 812 + } 813 + 814 + #define _read_pp_level_info(name_str, name, level, offset, start, width, mult_factor)\ 815 + {\ 816 + u64 val, _mask;\ 817 + \ 818 + val = readq(power_domain_info->sst_base +\ 819 + power_domain_info->perf_levels[level].mmio_offset +\ 820 + (power_domain_info->feature_offsets.pp_offset * 8) + (offset));\ 821 + _mask = GENMASK_ULL((start + width - 1), start);\ 822 + val &= _mask; \ 823 + val >>= start;\ 824 + name = (val * mult_factor);\ 825 + } 826 + 827 + #define SST_PP_INFO_0_OFFSET 0 828 + #define SST_PP_INFO_1_OFFSET 8 829 + #define SST_PP_INFO_2_OFFSET 16 830 + #define SST_PP_INFO_3_OFFSET 24 831 + 832 + /* SST_PP_INFO_4_OFFSET to SST_PP_INFO_9_OFFSET are trl levels */ 833 + #define SST_PP_INFO_4_OFFSET 32 834 + 835 + #define SST_PP_INFO_10_OFFSET 80 836 + #define SST_PP_INFO_11_OFFSET 88 837 + 838 + #define SST_PP_P1_SSE_START 0 839 + #define SST_PP_P1_SSE_WIDTH 8 840 + 841 + #define SST_PP_P1_AVX2_START 8 842 + #define SST_PP_P1_AVX2_WIDTH 8 843 + 844 + #define SST_PP_P1_AVX512_START 16 845 + #define SST_PP_P1_AVX512_WIDTH 8 846 + 847 + #define SST_PP_P1_AMX_START 24 848 + #define SST_PP_P1_AMX_WIDTH 8 849 + 850 + #define SST_PP_TDP_START 32 851 + #define SST_PP_TDP_WIDTH 15 852 + 853 + #define SST_PP_T_PROCHOT_START 47 854 + #define SST_PP_T_PROCHOT_WIDTH 8 855 + 856 + #define SST_PP_MAX_MEMORY_FREQ_START 55 857 + #define SST_PP_MAX_MEMORY_FREQ_WIDTH 7 858 + 859 + #define SST_PP_COOLING_TYPE_START 62 860 + #define SST_PP_COOLING_TYPE_WIDTH 2 861 + 862 + #define SST_PP_TRL_0_RATIO_0_START 0 863 + #define SST_PP_TRL_0_RATIO_0_WIDTH 8 864 + 865 + #define SST_PP_TRL_CORES_BUCKET_0_START 0 866 + #define SST_PP_TRL_CORES_BUCKET_0_WIDTH 8 867 + 868 + #define SST_PP_CORE_RATIO_P0_START 0 869 + #define SST_PP_CORE_RATIO_P0_WIDTH 8 870 + 871 + #define SST_PP_CORE_RATIO_P1_START 8 872 + #define SST_PP_CORE_RATIO_P1_WIDTH 8 873 + 874 + #define SST_PP_CORE_RATIO_PN_START 16 875 + #define SST_PP_CORE_RATIO_PN_WIDTH 8 876 + 877 + #define SST_PP_CORE_RATIO_PM_START 24 878 + #define SST_PP_CORE_RATIO_PM_WIDTH 8 879 + 880 + #define SST_PP_CORE_RATIO_P0_FABRIC_START 32 881 + #define SST_PP_CORE_RATIO_P0_FABRIC_WIDTH 8 882 + 883 + #define SST_PP_CORE_RATIO_P1_FABRIC_START 40 884 + #define SST_PP_CORE_RATIO_P1_FABRIC_WIDTH 8 885 + 886 + #define SST_PP_CORE_RATIO_PM_FABRIC_START 48 887 + #define SST_PP_CORE_RATIO_PM_FABRIC_WIDTH 8 888 + 889 + static int isst_if_get_perf_level_info(void __user *argp) 890 + { 891 + struct isst_perf_level_data_info perf_level; 892 + struct tpmi_per_power_domain_info *power_domain_info; 893 + int i, j; 894 + 895 + if (copy_from_user(&perf_level, argp, sizeof(perf_level))) 896 + return -EFAULT; 897 + 898 + power_domain_info = get_instance(perf_level.socket_id, perf_level.power_domain_id); 899 + if (!power_domain_info) 900 + return -EINVAL; 901 + 902 + if (perf_level.level > power_domain_info->max_level) 903 + return -EINVAL; 904 + 905 + if (!(power_domain_info->pp_header.level_en_mask & BIT(perf_level.level))) 906 + return -EINVAL; 907 + 908 + _read_pp_level_info("tdp_ratio", perf_level.tdp_ratio, perf_level.level, 909 + SST_PP_INFO_0_OFFSET, SST_PP_P1_SSE_START, SST_PP_P1_SSE_WIDTH, 910 + SST_MUL_FACTOR_NONE) 911 + _read_pp_level_info("base_freq_mhz", perf_level.base_freq_mhz, perf_level.level, 912 + SST_PP_INFO_0_OFFSET, SST_PP_P1_SSE_START, SST_PP_P1_SSE_WIDTH, 913 + SST_MUL_FACTOR_FREQ) 914 + _read_pp_level_info("base_freq_avx2_mhz", perf_level.base_freq_avx2_mhz, perf_level.level, 915 + SST_PP_INFO_0_OFFSET, SST_PP_P1_AVX2_START, SST_PP_P1_AVX2_WIDTH, 916 + SST_MUL_FACTOR_FREQ) 917 + _read_pp_level_info("base_freq_avx512_mhz", perf_level.base_freq_avx512_mhz, 918 + perf_level.level, SST_PP_INFO_0_OFFSET, SST_PP_P1_AVX512_START, 919 + SST_PP_P1_AVX512_WIDTH, SST_MUL_FACTOR_FREQ) 920 + _read_pp_level_info("base_freq_amx_mhz", perf_level.base_freq_amx_mhz, perf_level.level, 921 + SST_PP_INFO_0_OFFSET, SST_PP_P1_AMX_START, SST_PP_P1_AMX_WIDTH, 922 + SST_MUL_FACTOR_FREQ) 923 + 924 + _read_pp_level_info("thermal_design_power_w", perf_level.thermal_design_power_w, 925 + perf_level.level, SST_PP_INFO_1_OFFSET, SST_PP_TDP_START, 926 + SST_PP_TDP_WIDTH, SST_MUL_FACTOR_NONE) 927 + perf_level.thermal_design_power_w /= 8; /* units are in 1/8th watt */ 928 + _read_pp_level_info("tjunction_max_c", perf_level.tjunction_max_c, perf_level.level, 929 + SST_PP_INFO_1_OFFSET, SST_PP_T_PROCHOT_START, SST_PP_T_PROCHOT_WIDTH, 930 + SST_MUL_FACTOR_NONE) 931 + _read_pp_level_info("max_memory_freq_mhz", perf_level.max_memory_freq_mhz, 932 + perf_level.level, SST_PP_INFO_1_OFFSET, SST_PP_MAX_MEMORY_FREQ_START, 933 + SST_PP_MAX_MEMORY_FREQ_WIDTH, SST_MUL_FACTOR_FREQ) 934 + _read_pp_level_info("cooling_type", perf_level.cooling_type, perf_level.level, 935 + SST_PP_INFO_1_OFFSET, SST_PP_COOLING_TYPE_START, 936 + SST_PP_COOLING_TYPE_WIDTH, SST_MUL_FACTOR_NONE) 937 + 938 + for (i = 0; i < TRL_MAX_LEVELS; ++i) { 939 + for (j = 0; j < TRL_MAX_BUCKETS; ++j) 940 + _read_pp_level_info("trl*_bucket*_freq_mhz", 941 + perf_level.trl_freq_mhz[i][j], perf_level.level, 942 + SST_PP_INFO_4_OFFSET + (i * SST_PP_TRL_0_RATIO_0_WIDTH), 943 + j * SST_PP_TRL_0_RATIO_0_WIDTH, 944 + SST_PP_TRL_0_RATIO_0_WIDTH, 945 + SST_MUL_FACTOR_FREQ); 946 + } 947 + 948 + for (i = 0; i < TRL_MAX_BUCKETS; ++i) 949 + _read_pp_level_info("bucket*_core_count", perf_level.bucket_core_counts[i], 950 + perf_level.level, SST_PP_INFO_10_OFFSET, 951 + SST_PP_TRL_CORES_BUCKET_0_WIDTH * i, 952 + SST_PP_TRL_CORES_BUCKET_0_WIDTH, SST_MUL_FACTOR_NONE) 953 + 954 + perf_level.max_buckets = TRL_MAX_BUCKETS; 955 + perf_level.max_trl_levels = TRL_MAX_LEVELS; 956 + 957 + _read_pp_level_info("p0_freq_mhz", perf_level.p0_freq_mhz, perf_level.level, 958 + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_P0_START, 959 + SST_PP_CORE_RATIO_P0_WIDTH, SST_MUL_FACTOR_FREQ) 960 + _read_pp_level_info("p1_freq_mhz", perf_level.p1_freq_mhz, perf_level.level, 961 + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_P1_START, 962 + SST_PP_CORE_RATIO_P1_WIDTH, SST_MUL_FACTOR_FREQ) 963 + _read_pp_level_info("pn_freq_mhz", perf_level.pn_freq_mhz, perf_level.level, 964 + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_PN_START, 965 + SST_PP_CORE_RATIO_PN_WIDTH, SST_MUL_FACTOR_FREQ) 966 + _read_pp_level_info("pm_freq_mhz", perf_level.pm_freq_mhz, perf_level.level, 967 + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_PM_START, 968 + SST_PP_CORE_RATIO_PM_WIDTH, SST_MUL_FACTOR_FREQ) 969 + _read_pp_level_info("p0_fabric_freq_mhz", perf_level.p0_fabric_freq_mhz, 970 + perf_level.level, SST_PP_INFO_11_OFFSET, 971 + SST_PP_CORE_RATIO_P0_FABRIC_START, 972 + SST_PP_CORE_RATIO_P0_FABRIC_WIDTH, SST_MUL_FACTOR_FREQ) 973 + _read_pp_level_info("p1_fabric_freq_mhz", perf_level.p1_fabric_freq_mhz, 974 + perf_level.level, SST_PP_INFO_11_OFFSET, 975 + SST_PP_CORE_RATIO_P1_FABRIC_START, 976 + SST_PP_CORE_RATIO_P1_FABRIC_WIDTH, SST_MUL_FACTOR_FREQ) 977 + _read_pp_level_info("pm_fabric_freq_mhz", perf_level.pm_fabric_freq_mhz, 978 + perf_level.level, SST_PP_INFO_11_OFFSET, 979 + SST_PP_CORE_RATIO_PM_FABRIC_START, 980 + SST_PP_CORE_RATIO_PM_FABRIC_WIDTH, SST_MUL_FACTOR_FREQ) 981 + 982 + if (copy_to_user(argp, &perf_level, sizeof(perf_level))) 983 + return -EFAULT; 984 + 985 + return 0; 986 + } 987 + 988 + #define SST_PP_FUSED_CORE_COUNT_START 0 989 + #define SST_PP_FUSED_CORE_COUNT_WIDTH 8 990 + 991 + #define SST_PP_RSLVD_CORE_COUNT_START 8 992 + #define SST_PP_RSLVD_CORE_COUNT_WIDTH 8 993 + 994 + #define SST_PP_RSLVD_CORE_MASK_START 0 995 + #define SST_PP_RSLVD_CORE_MASK_WIDTH 64 996 + 997 + static int isst_if_get_perf_level_mask(void __user *argp) 998 + { 999 + static struct isst_perf_level_cpu_mask cpumask; 1000 + struct tpmi_per_power_domain_info *power_domain_info; 1001 + u64 mask; 1002 + 1003 + if (copy_from_user(&cpumask, argp, sizeof(cpumask))) 1004 + return -EFAULT; 1005 + 1006 + power_domain_info = get_instance(cpumask.socket_id, cpumask.power_domain_id); 1007 + if (!power_domain_info) 1008 + return -EINVAL; 1009 + 1010 + _read_pp_level_info("mask", mask, cpumask.level, SST_PP_INFO_2_OFFSET, 1011 + SST_PP_RSLVD_CORE_MASK_START, SST_PP_RSLVD_CORE_MASK_WIDTH, 1012 + SST_MUL_FACTOR_NONE) 1013 + 1014 + cpumask.mask = mask; 1015 + 1016 + if (!cpumask.punit_cpu_map) 1017 + return -EOPNOTSUPP; 1018 + 1019 + if (copy_to_user(argp, &cpumask, sizeof(cpumask))) 1020 + return -EFAULT; 1021 + 1022 + return 0; 1023 + } 1024 + 1025 + #define SST_BF_INFO_0_OFFSET 0 1026 + #define SST_BF_INFO_1_OFFSET 8 1027 + 1028 + #define SST_BF_P1_HIGH_START 13 1029 + #define SST_BF_P1_HIGH_WIDTH 8 1030 + 1031 + #define SST_BF_P1_LOW_START 21 1032 + #define SST_BF_P1_LOW_WIDTH 8 1033 + 1034 + #define SST_BF_T_PROHOT_START 38 1035 + #define SST_BF_T_PROHOT_WIDTH 8 1036 + 1037 + #define SST_BF_TDP_START 46 1038 + #define SST_BF_TDP_WIDTH 15 1039 + 1040 + static int isst_if_get_base_freq_info(void __user *argp) 1041 + { 1042 + static struct isst_base_freq_info base_freq; 1043 + struct tpmi_per_power_domain_info *power_domain_info; 1044 + 1045 + if (copy_from_user(&base_freq, argp, sizeof(base_freq))) 1046 + return -EFAULT; 1047 + 1048 + power_domain_info = get_instance(base_freq.socket_id, base_freq.power_domain_id); 1049 + if (!power_domain_info) 1050 + return -EINVAL; 1051 + 1052 + if (base_freq.level > power_domain_info->max_level) 1053 + return -EINVAL; 1054 + 1055 + _read_bf_level_info("p1_high", base_freq.high_base_freq_mhz, base_freq.level, 1056 + SST_BF_INFO_0_OFFSET, SST_BF_P1_HIGH_START, SST_BF_P1_HIGH_WIDTH, 1057 + SST_MUL_FACTOR_FREQ) 1058 + _read_bf_level_info("p1_low", base_freq.low_base_freq_mhz, base_freq.level, 1059 + SST_BF_INFO_0_OFFSET, SST_BF_P1_LOW_START, SST_BF_P1_LOW_WIDTH, 1060 + SST_MUL_FACTOR_FREQ) 1061 + _read_bf_level_info("BF-TJ", base_freq.tjunction_max_c, base_freq.level, 1062 + SST_BF_INFO_0_OFFSET, SST_BF_T_PROHOT_START, SST_BF_T_PROHOT_WIDTH, 1063 + SST_MUL_FACTOR_NONE) 1064 + _read_bf_level_info("BF-tdp", base_freq.thermal_design_power_w, base_freq.level, 1065 + SST_BF_INFO_0_OFFSET, SST_BF_TDP_START, SST_BF_TDP_WIDTH, 1066 + SST_MUL_FACTOR_NONE) 1067 + base_freq.thermal_design_power_w /= 8; /*unit = 1/8th watt*/ 1068 + 1069 + if (copy_to_user(argp, &base_freq, sizeof(base_freq))) 1070 + return -EFAULT; 1071 + 1072 + return 0; 1073 + } 1074 + 1075 + #define P1_HI_CORE_MASK_START 0 1076 + #define P1_HI_CORE_MASK_WIDTH 64 1077 + 1078 + static int isst_if_get_base_freq_mask(void __user *argp) 1079 + { 1080 + static struct isst_perf_level_cpu_mask cpumask; 1081 + struct tpmi_per_power_domain_info *power_domain_info; 1082 + u64 mask; 1083 + 1084 + if (copy_from_user(&cpumask, argp, sizeof(cpumask))) 1085 + return -EFAULT; 1086 + 1087 + power_domain_info = get_instance(cpumask.socket_id, cpumask.power_domain_id); 1088 + if (!power_domain_info) 1089 + return -EINVAL; 1090 + 1091 + _read_bf_level_info("BF-cpumask", mask, cpumask.level, SST_BF_INFO_1_OFFSET, 1092 + P1_HI_CORE_MASK_START, P1_HI_CORE_MASK_WIDTH, 1093 + SST_MUL_FACTOR_NONE) 1094 + 1095 + cpumask.mask = mask; 1096 + 1097 + if (!cpumask.punit_cpu_map) 1098 + return -EOPNOTSUPP; 1099 + 1100 + if (copy_to_user(argp, &cpumask, sizeof(cpumask))) 1101 + return -EFAULT; 1102 + 1103 + return 0; 1104 + } 1105 + 1106 + static int isst_if_get_tpmi_instance_count(void __user *argp) 1107 + { 1108 + struct isst_tpmi_instance_count tpmi_inst; 1109 + struct tpmi_sst_struct *sst_inst; 1110 + int i; 1111 + 1112 + if (copy_from_user(&tpmi_inst, argp, sizeof(tpmi_inst))) 1113 + return -EFAULT; 1114 + 1115 + if (tpmi_inst.socket_id >= topology_max_packages()) 1116 + return -EINVAL; 1117 + 1118 + tpmi_inst.count = isst_common.sst_inst[tpmi_inst.socket_id]->number_of_power_domains; 1119 + 1120 + sst_inst = isst_common.sst_inst[tpmi_inst.socket_id]; 1121 + tpmi_inst.valid_mask = 0; 1122 + for (i = 0; i < sst_inst->number_of_power_domains; ++i) { 1123 + struct tpmi_per_power_domain_info *pd_info; 1124 + 1125 + pd_info = &sst_inst->power_domain_info[i]; 1126 + if (pd_info->sst_base) 1127 + tpmi_inst.valid_mask |= BIT(i); 1128 + } 1129 + 1130 + if (copy_to_user(argp, &tpmi_inst, sizeof(tpmi_inst))) 1131 + return -EFAULT; 1132 + 1133 + return 0; 1134 + } 1135 + 1136 + #define SST_TF_INFO_0_OFFSET 0 1137 + #define SST_TF_INFO_1_OFFSET 8 1138 + #define SST_TF_INFO_2_OFFSET 16 1139 + 1140 + #define SST_TF_MAX_LP_CLIP_RATIOS TRL_MAX_LEVELS 1141 + 1142 + #define SST_TF_LP_CLIP_RATIO_0_START 16 1143 + #define SST_TF_LP_CLIP_RATIO_0_WIDTH 8 1144 + 1145 + #define SST_TF_RATIO_0_START 0 1146 + #define SST_TF_RATIO_0_WIDTH 8 1147 + 1148 + #define SST_TF_NUM_CORE_0_START 0 1149 + #define SST_TF_NUM_CORE_0_WIDTH 8 1150 + 1151 + static int isst_if_get_turbo_freq_info(void __user *argp) 1152 + { 1153 + static struct isst_turbo_freq_info turbo_freq; 1154 + struct tpmi_per_power_domain_info *power_domain_info; 1155 + int i, j; 1156 + 1157 + if (copy_from_user(&turbo_freq, argp, sizeof(turbo_freq))) 1158 + return -EFAULT; 1159 + 1160 + power_domain_info = get_instance(turbo_freq.socket_id, turbo_freq.power_domain_id); 1161 + if (!power_domain_info) 1162 + return -EINVAL; 1163 + 1164 + if (turbo_freq.level > power_domain_info->max_level) 1165 + return -EINVAL; 1166 + 1167 + turbo_freq.max_buckets = TRL_MAX_BUCKETS; 1168 + turbo_freq.max_trl_levels = TRL_MAX_LEVELS; 1169 + turbo_freq.max_clip_freqs = SST_TF_MAX_LP_CLIP_RATIOS; 1170 + 1171 + for (i = 0; i < turbo_freq.max_clip_freqs; ++i) 1172 + _read_tf_level_info("lp_clip*", turbo_freq.lp_clip_freq_mhz[i], 1173 + turbo_freq.level, SST_TF_INFO_0_OFFSET, 1174 + SST_TF_LP_CLIP_RATIO_0_START + 1175 + (i * SST_TF_LP_CLIP_RATIO_0_WIDTH), 1176 + SST_TF_LP_CLIP_RATIO_0_WIDTH, SST_MUL_FACTOR_FREQ) 1177 + 1178 + for (i = 0; i < TRL_MAX_LEVELS; ++i) { 1179 + for (j = 0; j < TRL_MAX_BUCKETS; ++j) 1180 + _read_tf_level_info("cydn*_bucket_*_trl", 1181 + turbo_freq.trl_freq_mhz[i][j], turbo_freq.level, 1182 + SST_TF_INFO_2_OFFSET + (i * SST_TF_RATIO_0_WIDTH), 1183 + j * SST_TF_RATIO_0_WIDTH, SST_TF_RATIO_0_WIDTH, 1184 + SST_MUL_FACTOR_FREQ) 1185 + } 1186 + 1187 + for (i = 0; i < TRL_MAX_BUCKETS; ++i) 1188 + _read_tf_level_info("bucket_*_core_count", turbo_freq.bucket_core_counts[i], 1189 + turbo_freq.level, SST_TF_INFO_1_OFFSET, 1190 + SST_TF_NUM_CORE_0_WIDTH * i, SST_TF_NUM_CORE_0_WIDTH, 1191 + SST_MUL_FACTOR_NONE) 1192 + 1193 + if (copy_to_user(argp, &turbo_freq, sizeof(turbo_freq))) 1194 + return -EFAULT; 1195 + 1196 + return 0; 1197 + } 1198 + 1199 + static long isst_if_def_ioctl(struct file *file, unsigned int cmd, 1200 + unsigned long arg) 1201 + { 1202 + void __user *argp = (void __user *)arg; 1203 + long ret = -ENOTTY; 1204 + 1205 + mutex_lock(&isst_tpmi_dev_lock); 1206 + switch (cmd) { 1207 + case ISST_IF_COUNT_TPMI_INSTANCES: 1208 + ret = isst_if_get_tpmi_instance_count(argp); 1209 + break; 1210 + case ISST_IF_CORE_POWER_STATE: 1211 + ret = isst_if_core_power_state(argp); 1212 + break; 1213 + case ISST_IF_CLOS_PARAM: 1214 + ret = isst_if_clos_param(argp); 1215 + break; 1216 + case ISST_IF_CLOS_ASSOC: 1217 + ret = isst_if_clos_assoc(argp); 1218 + break; 1219 + case ISST_IF_PERF_LEVELS: 1220 + ret = isst_if_get_perf_level(argp); 1221 + break; 1222 + case ISST_IF_PERF_SET_LEVEL: 1223 + ret = isst_if_set_perf_level(argp); 1224 + break; 1225 + case ISST_IF_PERF_SET_FEATURE: 1226 + ret = isst_if_set_perf_feature(argp); 1227 + break; 1228 + case ISST_IF_GET_PERF_LEVEL_INFO: 1229 + ret = isst_if_get_perf_level_info(argp); 1230 + break; 1231 + case ISST_IF_GET_PERF_LEVEL_CPU_MASK: 1232 + ret = isst_if_get_perf_level_mask(argp); 1233 + break; 1234 + case ISST_IF_GET_BASE_FREQ_INFO: 1235 + ret = isst_if_get_base_freq_info(argp); 1236 + break; 1237 + case ISST_IF_GET_BASE_FREQ_CPU_MASK: 1238 + ret = isst_if_get_base_freq_mask(argp); 1239 + break; 1240 + case ISST_IF_GET_TURBO_FREQ_INFO: 1241 + ret = isst_if_get_turbo_freq_info(argp); 1242 + break; 1243 + default: 1244 + break; 1245 + } 1246 + mutex_unlock(&isst_tpmi_dev_lock); 1247 + 1248 + return ret; 1249 + } 1250 + 1251 + #define TPMI_SST_AUTO_SUSPEND_DELAY_MS 2000 1252 + 1253 + int tpmi_sst_dev_add(struct auxiliary_device *auxdev) 1254 + { 1255 + struct intel_tpmi_plat_info *plat_info; 1256 + struct tpmi_sst_struct *tpmi_sst; 1257 + int i, ret, pkg = 0, inst = 0; 1258 + int num_resources; 1259 + 1260 + plat_info = tpmi_get_platform_data(auxdev); 1261 + if (!plat_info) { 1262 + dev_err(&auxdev->dev, "No platform info\n"); 1263 + return -EINVAL; 1264 + } 1265 + 1266 + pkg = plat_info->package_id; 1267 + if (pkg >= topology_max_packages()) { 1268 + dev_err(&auxdev->dev, "Invalid package id :%x\n", pkg); 1269 + return -EINVAL; 1270 + } 1271 + 1272 + if (isst_common.sst_inst[pkg]) 1273 + return -EEXIST; 1274 + 1275 + num_resources = tpmi_get_resource_count(auxdev); 1276 + 1277 + if (!num_resources) 1278 + return -EINVAL; 1279 + 1280 + tpmi_sst = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_sst), GFP_KERNEL); 1281 + if (!tpmi_sst) 1282 + return -ENOMEM; 1283 + 1284 + tpmi_sst->power_domain_info = devm_kcalloc(&auxdev->dev, num_resources, 1285 + sizeof(*tpmi_sst->power_domain_info), 1286 + GFP_KERNEL); 1287 + if (!tpmi_sst->power_domain_info) 1288 + return -ENOMEM; 1289 + 1290 + tpmi_sst->number_of_power_domains = num_resources; 1291 + 1292 + for (i = 0; i < num_resources; ++i) { 1293 + struct resource *res; 1294 + 1295 + res = tpmi_get_resource_at_index(auxdev, i); 1296 + if (!res) { 1297 + tpmi_sst->power_domain_info[i].sst_base = NULL; 1298 + continue; 1299 + } 1300 + 1301 + tpmi_sst->power_domain_info[i].package_id = pkg; 1302 + tpmi_sst->power_domain_info[i].power_domain_id = i; 1303 + tpmi_sst->power_domain_info[i].auxdev = auxdev; 1304 + tpmi_sst->power_domain_info[i].sst_base = devm_ioremap_resource(&auxdev->dev, res); 1305 + if (IS_ERR(tpmi_sst->power_domain_info[i].sst_base)) 1306 + return PTR_ERR(tpmi_sst->power_domain_info[i].sst_base); 1307 + 1308 + ret = sst_main(auxdev, &tpmi_sst->power_domain_info[i]); 1309 + if (ret) { 1310 + devm_iounmap(&auxdev->dev, tpmi_sst->power_domain_info[i].sst_base); 1311 + tpmi_sst->power_domain_info[i].sst_base = NULL; 1312 + continue; 1313 + } 1314 + 1315 + ++inst; 1316 + } 1317 + 1318 + if (!inst) 1319 + return -ENODEV; 1320 + 1321 + tpmi_sst->package_id = pkg; 1322 + auxiliary_set_drvdata(auxdev, tpmi_sst); 1323 + 1324 + mutex_lock(&isst_tpmi_dev_lock); 1325 + if (isst_common.max_index < pkg) 1326 + isst_common.max_index = pkg; 1327 + isst_common.sst_inst[pkg] = tpmi_sst; 1328 + mutex_unlock(&isst_tpmi_dev_lock); 1329 + 1330 + return 0; 1331 + } 1332 + EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_add, INTEL_TPMI_SST); 1333 + 1334 + void tpmi_sst_dev_remove(struct auxiliary_device *auxdev) 1335 + { 1336 + struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); 1337 + 1338 + mutex_lock(&isst_tpmi_dev_lock); 1339 + isst_common.sst_inst[tpmi_sst->package_id] = NULL; 1340 + mutex_unlock(&isst_tpmi_dev_lock); 1341 + } 1342 + EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, INTEL_TPMI_SST); 1343 + 1344 + void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev) 1345 + { 1346 + struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); 1347 + struct tpmi_per_power_domain_info *power_domain_info = tpmi_sst->power_domain_info; 1348 + void __iomem *cp_base; 1349 + 1350 + cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset; 1351 + power_domain_info->saved_sst_cp_control = readq(cp_base + SST_CP_CONTROL_OFFSET); 1352 + 1353 + memcpy_fromio(power_domain_info->saved_clos_configs, cp_base + SST_CLOS_CONFIG_0_OFFSET, 1354 + sizeof(power_domain_info->saved_clos_configs)); 1355 + 1356 + memcpy_fromio(power_domain_info->saved_clos_assocs, cp_base + SST_CLOS_ASSOC_0_OFFSET, 1357 + sizeof(power_domain_info->saved_clos_assocs)); 1358 + 1359 + power_domain_info->saved_pp_control = readq(power_domain_info->sst_base + 1360 + power_domain_info->sst_header.pp_offset + 1361 + SST_PP_CONTROL_OFFSET); 1362 + } 1363 + EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_suspend, INTEL_TPMI_SST); 1364 + 1365 + void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) 1366 + { 1367 + struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); 1368 + struct tpmi_per_power_domain_info *power_domain_info = tpmi_sst->power_domain_info; 1369 + void __iomem *cp_base; 1370 + 1371 + cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset; 1372 + writeq(power_domain_info->saved_sst_cp_control, cp_base + SST_CP_CONTROL_OFFSET); 1373 + 1374 + memcpy_toio(cp_base + SST_CLOS_CONFIG_0_OFFSET, power_domain_info->saved_clos_configs, 1375 + sizeof(power_domain_info->saved_clos_configs)); 1376 + 1377 + memcpy_toio(cp_base + SST_CLOS_ASSOC_0_OFFSET, power_domain_info->saved_clos_assocs, 1378 + sizeof(power_domain_info->saved_clos_assocs)); 1379 + 1380 + writeq(power_domain_info->saved_pp_control, power_domain_info->sst_base + 1381 + power_domain_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET); 1382 + } 1383 + EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_resume, INTEL_TPMI_SST); 1384 + 1385 + #define ISST_TPMI_API_VERSION 0x02 1386 + 1387 + int tpmi_sst_init(void) 1388 + { 1389 + struct isst_if_cmd_cb cb; 1390 + int ret = 0; 1391 + 1392 + mutex_lock(&isst_tpmi_dev_lock); 1393 + 1394 + if (isst_core_usage_count) { 1395 + ++isst_core_usage_count; 1396 + goto init_done; 1397 + } 1398 + 1399 + isst_common.sst_inst = kcalloc(topology_max_packages(), 1400 + sizeof(*isst_common.sst_inst), 1401 + GFP_KERNEL); 1402 + if (!isst_common.sst_inst) { 1403 + ret = -ENOMEM; 1404 + goto init_done; 1405 + } 1406 + 1407 + memset(&cb, 0, sizeof(cb)); 1408 + cb.cmd_size = sizeof(struct isst_if_io_reg); 1409 + cb.offset = offsetof(struct isst_if_io_regs, io_reg); 1410 + cb.cmd_callback = NULL; 1411 + cb.api_version = ISST_TPMI_API_VERSION; 1412 + cb.def_ioctl = isst_if_def_ioctl; 1413 + cb.owner = THIS_MODULE; 1414 + ret = isst_if_cdev_register(ISST_IF_DEV_TPMI, &cb); 1415 + if (ret) 1416 + kfree(isst_common.sst_inst); 1417 + init_done: 1418 + mutex_unlock(&isst_tpmi_dev_lock); 1419 + return ret; 1420 + } 1421 + EXPORT_SYMBOL_NS_GPL(tpmi_sst_init, INTEL_TPMI_SST); 1422 + 1423 + void tpmi_sst_exit(void) 1424 + { 1425 + mutex_lock(&isst_tpmi_dev_lock); 1426 + if (isst_core_usage_count) 1427 + --isst_core_usage_count; 1428 + 1429 + if (!isst_core_usage_count) { 1430 + isst_if_cdev_unregister(ISST_IF_DEV_TPMI); 1431 + kfree(isst_common.sst_inst); 1432 + } 1433 + mutex_unlock(&isst_tpmi_dev_lock); 1434 + } 1435 + EXPORT_SYMBOL_NS_GPL(tpmi_sst_exit, INTEL_TPMI_SST); 1436 + 1437 + MODULE_IMPORT_NS(INTEL_TPMI); 1438 + MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN); 1439 + 1440 + MODULE_LICENSE("GPL");
+18
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Intel Speed Select Interface: Drivers Internal defines 4 + * Copyright (c) 2023, Intel Corporation. 5 + * All rights reserved. 6 + * 7 + */ 8 + 9 + #ifndef _ISST_TPMI_CORE_H 10 + #define _ISST_TPMI_CORE_H 11 + 12 + int tpmi_sst_init(void); 13 + void tpmi_sst_exit(void); 14 + int tpmi_sst_dev_add(struct auxiliary_device *auxdev); 15 + void tpmi_sst_dev_remove(struct auxiliary_device *auxdev); 16 + void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev); 17 + void tpmi_sst_dev_resume(struct auxiliary_device *auxdev); 18 + #endif
+2 -3
drivers/platform/x86/intel/telemetry/pltdrv.c
··· 1156 1156 return ret; 1157 1157 } 1158 1158 1159 - static int telemetry_pltdrv_remove(struct platform_device *pdev) 1159 + static void telemetry_pltdrv_remove(struct platform_device *pdev) 1160 1160 { 1161 1161 telemetry_clear_pltdata(); 1162 - return 0; 1163 1162 } 1164 1163 1165 1164 static struct platform_driver telemetry_soc_driver = { 1166 1165 .probe = telemetry_pltdrv_probe, 1167 - .remove = telemetry_pltdrv_remove, 1166 + .remove_new = telemetry_pltdrv_remove, 1168 1167 .driver = { 1169 1168 .name = DRIVER_NAME, 1170 1169 },
+7
drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
··· 204 204 X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL), 205 205 X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL), 206 206 X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, NULL), 207 + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), 208 + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), 209 + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), 210 + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL), 211 + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL), 212 + X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL), 213 + X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL), 207 214 {} 208 215 }; 209 216 MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids);
+2 -8
drivers/platform/x86/intel/vbtn.c
··· 325 325 return 0; 326 326 } 327 327 328 - static int intel_vbtn_remove(struct platform_device *device) 328 + static void intel_vbtn_remove(struct platform_device *device) 329 329 { 330 330 acpi_handle handle = ACPI_HANDLE(&device->dev); 331 331 332 332 device_init_wakeup(&device->dev, false); 333 333 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); 334 - 335 - /* 336 - * Even if we failed to shut off the event stream, we can still 337 - * safely detach from the device. 338 - */ 339 - return 0; 340 334 } 341 335 342 336 static int intel_vbtn_pm_prepare(struct device *dev) ··· 371 377 .pm = &intel_vbtn_pm_ops, 372 378 }, 373 379 .probe = intel_vbtn_probe, 374 - .remove = intel_vbtn_remove, 380 + .remove_new = intel_vbtn_remove, 375 381 }; 376 382 377 383 static acpi_status __init
+31 -44
drivers/platform/x86/intel/vsec.c
··· 67 67 VSEC_ID_TPMI = 66, 68 68 }; 69 69 70 - static enum intel_vsec_id intel_vsec_allow_list[] = { 71 - VSEC_ID_TELEMETRY, 72 - VSEC_ID_WATCHER, 73 - VSEC_ID_CRASHLOG, 74 - VSEC_ID_SDSI, 75 - VSEC_ID_TPMI, 76 - }; 77 - 78 70 static const char *intel_vsec_name(enum intel_vsec_id id) 79 71 { 80 72 switch (id) { ··· 90 98 } 91 99 } 92 100 93 - static bool intel_vsec_allowed(u16 id) 94 - { 95 - int i; 96 - 97 - for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++) 98 - if (intel_vsec_allow_list[i] == id) 99 - return true; 100 - 101 - return false; 102 - } 103 - 104 - static bool intel_vsec_disabled(u16 id, unsigned long quirks) 101 + static bool intel_vsec_supported(u16 id, unsigned long caps) 105 102 { 106 103 switch (id) { 104 + case VSEC_ID_TELEMETRY: 105 + return !!(caps & VSEC_CAP_TELEMETRY); 107 106 case VSEC_ID_WATCHER: 108 - return !!(quirks & VSEC_QUIRK_NO_WATCHER); 109 - 107 + return !!(caps & VSEC_CAP_WATCHER); 110 108 case VSEC_ID_CRASHLOG: 111 - return !!(quirks & VSEC_QUIRK_NO_CRASHLOG); 112 - 109 + return !!(caps & VSEC_CAP_CRASHLOG); 110 + case VSEC_ID_SDSI: 111 + return !!(caps & VSEC_CAP_SDSI); 112 + case VSEC_ID_TPMI: 113 + return !!(caps & VSEC_CAP_TPMI); 113 114 default: 114 115 return false; 115 116 } ··· 154 169 155 170 ret = auxiliary_device_init(auxdev); 156 171 if (ret < 0) { 157 - mutex_lock(&vsec_ida_lock); 158 - ida_free(intel_vsec_dev->ida, auxdev->id); 159 - mutex_unlock(&vsec_ida_lock); 160 - kfree(intel_vsec_dev->resource); 161 - kfree(intel_vsec_dev); 172 + intel_vsec_dev_release(&auxdev->dev); 162 173 return ret; 163 174 } 164 175 ··· 187 206 unsigned long quirks = info->quirks; 188 207 int i; 189 208 190 - if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) 209 + if (!intel_vsec_supported(header->id, info->caps)) 191 210 return -EINVAL; 192 211 193 212 if (!header->num_entries) { ··· 242 261 static bool intel_vsec_walk_header(struct pci_dev *pdev, 243 262 struct intel_vsec_platform_info *info) 244 263 { 245 - struct intel_vsec_header **header = info->capabilities; 264 + struct intel_vsec_header **header = info->headers; 246 265 bool have_devices = false; 247 266 int ret; 248 267 249 268 for ( ; *header; header++) { 250 269 ret = intel_vsec_add_dev(pdev, *header, info); 251 270 if (ret) 252 - dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", 271 + dev_info(&pdev->dev, "Could not add device for VSEC id %d\n", 253 272 (*header)->id); 254 273 else 255 274 have_devices = true; ··· 384 403 return 0; 385 404 } 386 405 387 - /* TGL info */ 388 - static const struct intel_vsec_platform_info tgl_info = { 389 - .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | 390 - VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, 391 - }; 392 - 393 406 /* DG1 info */ 394 - static struct intel_vsec_header dg1_telemetry = { 407 + static struct intel_vsec_header dg1_header = { 395 408 .length = 0x10, 396 409 .id = 2, 397 410 .num_entries = 1, ··· 394 419 .offset = 0x466000, 395 420 }; 396 421 397 - static struct intel_vsec_header *dg1_capabilities[] = { 398 - &dg1_telemetry, 422 + static struct intel_vsec_header *dg1_headers[] = { 423 + &dg1_header, 399 424 NULL 400 425 }; 401 426 402 427 static const struct intel_vsec_platform_info dg1_info = { 403 - .capabilities = dg1_capabilities, 428 + .caps = VSEC_CAP_TELEMETRY, 429 + .headers = dg1_headers, 404 430 .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, 405 431 }; 406 432 407 433 /* MTL info */ 408 434 static const struct intel_vsec_platform_info mtl_info = { 409 - .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG, 435 + .caps = VSEC_CAP_TELEMETRY, 436 + }; 437 + 438 + /* OOBMSM info */ 439 + static const struct intel_vsec_platform_info oobmsm_info = { 440 + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, 441 + }; 442 + 443 + /* TGL info */ 444 + static const struct intel_vsec_platform_info tgl_info = { 445 + .caps = VSEC_CAP_TELEMETRY, 446 + .quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, 410 447 }; 411 448 412 449 #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d ··· 433 446 { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, 434 447 { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, 435 448 { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, 436 - { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) }, 449 + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, 437 450 { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, 438 451 { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, 439 452 { }
+8 -1
drivers/platform/x86/intel/vsec.h
··· 5 5 #include <linux/auxiliary_bus.h> 6 6 #include <linux/bits.h> 7 7 8 + #define VSEC_CAP_TELEMETRY BIT(0) 9 + #define VSEC_CAP_WATCHER BIT(1) 10 + #define VSEC_CAP_CRASHLOG BIT(2) 11 + #define VSEC_CAP_SDSI BIT(3) 12 + #define VSEC_CAP_TPMI BIT(4) 13 + 8 14 struct pci_dev; 9 15 struct resource; 10 16 ··· 33 27 34 28 /* Platform specific data */ 35 29 struct intel_vsec_platform_info { 36 - struct intel_vsec_header **capabilities; 30 + struct intel_vsec_header **headers; 31 + unsigned long caps; 37 32 unsigned long quirks; 38 33 }; 39 34
+187
drivers/platform/x86/lenovo-ymc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * lenovo-ymc.c - Lenovo Yoga Mode Control driver 4 + * 5 + * Copyright © 2022 Gergo Koteles <soyer@irl.hu> 6 + */ 7 + 8 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 + 10 + #include <linux/acpi.h> 11 + #include <linux/dmi.h> 12 + #include <linux/input.h> 13 + #include <linux/input/sparse-keymap.h> 14 + #include <linux/wmi.h> 15 + #include "ideapad-laptop.h" 16 + 17 + #define LENOVO_YMC_EVENT_GUID "06129D99-6083-4164-81AD-F092F9D773A6" 18 + #define LENOVO_YMC_QUERY_GUID "09B0EE6E-C3FD-4243-8DA1-7911FF80BB8C" 19 + 20 + #define LENOVO_YMC_QUERY_INSTANCE 0 21 + #define LENOVO_YMC_QUERY_METHOD 0x01 22 + 23 + static bool ec_trigger __read_mostly; 24 + module_param(ec_trigger, bool, 0444); 25 + MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events"); 26 + 27 + static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = { 28 + { 29 + /* Lenovo Yoga 7 14ARB7 */ 30 + .matches = { 31 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 32 + DMI_MATCH(DMI_PRODUCT_NAME, "82QF"), 33 + }, 34 + }, 35 + { } 36 + }; 37 + 38 + struct lenovo_ymc_private { 39 + struct input_dev *input_dev; 40 + struct acpi_device *ec_acpi_dev; 41 + }; 42 + 43 + static void lenovo_ymc_trigger_ec(struct wmi_device *wdev, struct lenovo_ymc_private *priv) 44 + { 45 + int err; 46 + 47 + if (!priv->ec_acpi_dev) 48 + return; 49 + 50 + err = write_ec_cmd(priv->ec_acpi_dev->handle, VPCCMD_W_YMC, 1); 51 + if (err) 52 + dev_warn(&wdev->dev, "Could not write YMC: %d\n", err); 53 + } 54 + 55 + static const struct key_entry lenovo_ymc_keymap[] = { 56 + /* Laptop */ 57 + { KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } }, 58 + /* Tablet */ 59 + { KE_SW, 0x02, { .sw = { SW_TABLET_MODE, 1 } } }, 60 + /* Drawing Board */ 61 + { KE_SW, 0x03, { .sw = { SW_TABLET_MODE, 1 } } }, 62 + /* Tent */ 63 + { KE_SW, 0x04, { .sw = { SW_TABLET_MODE, 1 } } }, 64 + { KE_END }, 65 + }; 66 + 67 + static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data) 68 + { 69 + struct lenovo_ymc_private *priv = dev_get_drvdata(&wdev->dev); 70 + u32 input_val = 0; 71 + struct acpi_buffer input = { sizeof(input_val), &input_val }; 72 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 73 + union acpi_object *obj; 74 + acpi_status status; 75 + int code; 76 + 77 + status = wmi_evaluate_method(LENOVO_YMC_QUERY_GUID, 78 + LENOVO_YMC_QUERY_INSTANCE, 79 + LENOVO_YMC_QUERY_METHOD, 80 + &input, &output); 81 + 82 + if (ACPI_FAILURE(status)) { 83 + dev_warn(&wdev->dev, 84 + "Failed to evaluate query method: %s\n", 85 + acpi_format_exception(status)); 86 + return; 87 + } 88 + 89 + obj = output.pointer; 90 + 91 + if (obj->type != ACPI_TYPE_INTEGER) { 92 + dev_warn(&wdev->dev, 93 + "WMI event data is not an integer\n"); 94 + goto free_obj; 95 + } 96 + code = obj->integer.value; 97 + 98 + if (!sparse_keymap_report_event(priv->input_dev, code, 1, true)) 99 + dev_warn(&wdev->dev, "Unknown key %d pressed\n", code); 100 + 101 + free_obj: 102 + kfree(obj); 103 + lenovo_ymc_trigger_ec(wdev, priv); 104 + } 105 + 106 + static void acpi_dev_put_helper(void *p) { acpi_dev_put(p); } 107 + 108 + static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx) 109 + { 110 + struct lenovo_ymc_private *priv; 111 + struct input_dev *input_dev; 112 + int err; 113 + 114 + ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table); 115 + 116 + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 117 + if (!priv) 118 + return -ENOMEM; 119 + 120 + if (ec_trigger) { 121 + pr_debug("Lenovo YMC enable EC triggering.\n"); 122 + priv->ec_acpi_dev = acpi_dev_get_first_match_dev("VPC2004", NULL, -1); 123 + 124 + if (!priv->ec_acpi_dev) { 125 + dev_err(&wdev->dev, "Could not find EC ACPI device.\n"); 126 + return -ENODEV; 127 + } 128 + err = devm_add_action_or_reset(&wdev->dev, 129 + acpi_dev_put_helper, priv->ec_acpi_dev); 130 + if (err) { 131 + dev_err(&wdev->dev, 132 + "Could not clean up EC ACPI device: %d\n", err); 133 + return err; 134 + } 135 + } 136 + 137 + input_dev = devm_input_allocate_device(&wdev->dev); 138 + if (!input_dev) 139 + return -ENOMEM; 140 + 141 + input_dev->name = "Lenovo Yoga Tablet Mode Control switch"; 142 + input_dev->phys = LENOVO_YMC_EVENT_GUID "/input0"; 143 + input_dev->id.bustype = BUS_HOST; 144 + input_dev->dev.parent = &wdev->dev; 145 + err = sparse_keymap_setup(input_dev, lenovo_ymc_keymap, NULL); 146 + if (err) { 147 + dev_err(&wdev->dev, 148 + "Could not set up input device keymap: %d\n", err); 149 + return err; 150 + } 151 + 152 + err = input_register_device(input_dev); 153 + if (err) { 154 + dev_err(&wdev->dev, 155 + "Could not register input device: %d\n", err); 156 + return err; 157 + } 158 + 159 + priv->input_dev = input_dev; 160 + dev_set_drvdata(&wdev->dev, priv); 161 + 162 + /* Report the state for the first time on probe */ 163 + lenovo_ymc_trigger_ec(wdev, priv); 164 + lenovo_ymc_notify(wdev, NULL); 165 + return 0; 166 + } 167 + 168 + static const struct wmi_device_id lenovo_ymc_wmi_id_table[] = { 169 + { .guid_string = LENOVO_YMC_EVENT_GUID }, 170 + { } 171 + }; 172 + MODULE_DEVICE_TABLE(wmi, lenovo_ymc_wmi_id_table); 173 + 174 + static struct wmi_driver lenovo_ymc_driver = { 175 + .driver = { 176 + .name = "lenovo-ymc", 177 + }, 178 + .id_table = lenovo_ymc_wmi_id_table, 179 + .probe = lenovo_ymc_probe, 180 + .notify = lenovo_ymc_notify, 181 + }; 182 + 183 + module_wmi_driver(lenovo_ymc_driver); 184 + 185 + MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>"); 186 + MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver"); 187 + MODULE_LICENSE("GPL");
+897
drivers/platform/x86/msi-ec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + /* 4 + * msi-ec: MSI laptops' embedded controller driver. 5 + * 6 + * This driver allows various MSI laptops' functionalities to be 7 + * controlled from userspace. 8 + * 9 + * It contains EC memory configurations for different firmware versions 10 + * and exports battery charge thresholds to userspace. 11 + * 12 + * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es> 13 + * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev> 14 + * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com> 15 + */ 16 + 17 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 + 19 + #include "msi-ec.h" 20 + 21 + #include <acpi/battery.h> 22 + #include <linux/acpi.h> 23 + #include <linux/init.h> 24 + #include <linux/kernel.h> 25 + #include <linux/module.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/seq_file.h> 28 + #include <linux/string.h> 29 + 30 + static const char *const SM_ECO_NAME = "eco"; 31 + static const char *const SM_COMFORT_NAME = "comfort"; 32 + static const char *const SM_SPORT_NAME = "sport"; 33 + static const char *const SM_TURBO_NAME = "turbo"; 34 + 35 + static const char *const FM_AUTO_NAME = "auto"; 36 + static const char *const FM_SILENT_NAME = "silent"; 37 + static const char *const FM_BASIC_NAME = "basic"; 38 + static const char *const FM_ADVANCED_NAME = "advanced"; 39 + 40 + static const char * const ALLOWED_FW_0[] __initconst = { 41 + "14C1EMS1.012", 42 + "14C1EMS1.101", 43 + "14C1EMS1.102", 44 + NULL 45 + }; 46 + 47 + static struct msi_ec_conf CONF0 __initdata = { 48 + .allowed_fw = ALLOWED_FW_0, 49 + .charge_control = { 50 + .address = 0xef, 51 + .offset_start = 0x8a, 52 + .offset_end = 0x80, 53 + .range_min = 0x8a, 54 + .range_max = 0xe4, 55 + }, 56 + .webcam = { 57 + .address = 0x2e, 58 + .block_address = 0x2f, 59 + .bit = 1, 60 + }, 61 + .fn_super_swap = { 62 + .address = 0xbf, 63 + .bit = 4, 64 + }, 65 + .cooler_boost = { 66 + .address = 0x98, 67 + .bit = 7, 68 + }, 69 + .shift_mode = { 70 + .address = 0xf2, 71 + .modes = { 72 + { SM_ECO_NAME, 0xc2 }, 73 + { SM_COMFORT_NAME, 0xc1 }, 74 + { SM_SPORT_NAME, 0xc0 }, 75 + MSI_EC_MODE_NULL 76 + }, 77 + }, 78 + .super_battery = { 79 + .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing 80 + }, 81 + .fan_mode = { 82 + .address = 0xf4, 83 + .modes = { 84 + { FM_AUTO_NAME, 0x0d }, 85 + { FM_SILENT_NAME, 0x1d }, 86 + { FM_BASIC_NAME, 0x4d }, 87 + { FM_ADVANCED_NAME, 0x8d }, 88 + MSI_EC_MODE_NULL 89 + }, 90 + }, 91 + .cpu = { 92 + .rt_temp_address = 0x68, 93 + .rt_fan_speed_address = 0x71, 94 + .rt_fan_speed_base_min = 0x19, 95 + .rt_fan_speed_base_max = 0x37, 96 + .bs_fan_speed_address = 0x89, 97 + .bs_fan_speed_base_min = 0x00, 98 + .bs_fan_speed_base_max = 0x0f, 99 + }, 100 + .gpu = { 101 + .rt_temp_address = 0x80, 102 + .rt_fan_speed_address = 0x89, 103 + }, 104 + .leds = { 105 + .micmute_led_address = 0x2b, 106 + .mute_led_address = 0x2c, 107 + .bit = 2, 108 + }, 109 + .kbd_bl = { 110 + .bl_mode_address = 0x2c, // ? 111 + .bl_modes = { 0x00, 0x08 }, // ? 112 + .max_mode = 1, // ? 113 + .bl_state_address = 0xf3, 114 + .state_base_value = 0x80, 115 + .max_state = 3, 116 + }, 117 + }; 118 + 119 + static const char * const ALLOWED_FW_1[] __initconst = { 120 + "17F2EMS1.103", 121 + "17F2EMS1.104", 122 + "17F2EMS1.106", 123 + "17F2EMS1.107", 124 + NULL 125 + }; 126 + 127 + static struct msi_ec_conf CONF1 __initdata = { 128 + .allowed_fw = ALLOWED_FW_1, 129 + .charge_control = { 130 + .address = 0xef, 131 + .offset_start = 0x8a, 132 + .offset_end = 0x80, 133 + .range_min = 0x8a, 134 + .range_max = 0xe4, 135 + }, 136 + .webcam = { 137 + .address = 0x2e, 138 + .block_address = 0x2f, 139 + .bit = 1, 140 + }, 141 + .fn_super_swap = { 142 + .address = 0xbf, 143 + .bit = 4, 144 + }, 145 + .cooler_boost = { 146 + .address = 0x98, 147 + .bit = 7, 148 + }, 149 + .shift_mode = { 150 + .address = 0xf2, 151 + .modes = { 152 + { SM_ECO_NAME, 0xc2 }, 153 + { SM_COMFORT_NAME, 0xc1 }, 154 + { SM_SPORT_NAME, 0xc0 }, 155 + { SM_TURBO_NAME, 0xc4 }, 156 + MSI_EC_MODE_NULL 157 + }, 158 + }, 159 + .super_battery = { 160 + .address = MSI_EC_ADDR_UNKNOWN, 161 + }, 162 + .fan_mode = { 163 + .address = 0xf4, 164 + .modes = { 165 + { FM_AUTO_NAME, 0x0d }, 166 + { FM_BASIC_NAME, 0x4d }, 167 + { FM_ADVANCED_NAME, 0x8d }, 168 + MSI_EC_MODE_NULL 169 + }, 170 + }, 171 + .cpu = { 172 + .rt_temp_address = 0x68, 173 + .rt_fan_speed_address = 0x71, 174 + .rt_fan_speed_base_min = 0x19, 175 + .rt_fan_speed_base_max = 0x37, 176 + .bs_fan_speed_address = 0x89, 177 + .bs_fan_speed_base_min = 0x00, 178 + .bs_fan_speed_base_max = 0x0f, 179 + }, 180 + .gpu = { 181 + .rt_temp_address = 0x80, 182 + .rt_fan_speed_address = 0x89, 183 + }, 184 + .leds = { 185 + .micmute_led_address = 0x2b, 186 + .mute_led_address = 0x2c, 187 + .bit = 2, 188 + }, 189 + .kbd_bl = { 190 + .bl_mode_address = 0x2c, // ? 191 + .bl_modes = { 0x00, 0x08 }, // ? 192 + .max_mode = 1, // ? 193 + .bl_state_address = 0xf3, 194 + .state_base_value = 0x80, 195 + .max_state = 3, 196 + }, 197 + }; 198 + 199 + static const char * const ALLOWED_FW_2[] __initconst = { 200 + "1552EMS1.118", 201 + NULL 202 + }; 203 + 204 + static struct msi_ec_conf CONF2 __initdata = { 205 + .allowed_fw = ALLOWED_FW_2, 206 + .charge_control = { 207 + .address = 0xd7, 208 + .offset_start = 0x8a, 209 + .offset_end = 0x80, 210 + .range_min = 0x8a, 211 + .range_max = 0xe4, 212 + }, 213 + .webcam = { 214 + .address = 0x2e, 215 + .block_address = 0x2f, 216 + .bit = 1, 217 + }, 218 + .fn_super_swap = { 219 + .address = 0xe8, 220 + .bit = 4, 221 + }, 222 + .cooler_boost = { 223 + .address = 0x98, 224 + .bit = 7, 225 + }, 226 + .shift_mode = { 227 + .address = 0xf2, 228 + .modes = { 229 + { SM_ECO_NAME, 0xc2 }, 230 + { SM_COMFORT_NAME, 0xc1 }, 231 + { SM_SPORT_NAME, 0xc0 }, 232 + MSI_EC_MODE_NULL 233 + }, 234 + }, 235 + .super_battery = { 236 + .address = 0xeb, 237 + .mask = 0x0f, 238 + }, 239 + .fan_mode = { 240 + .address = 0xd4, 241 + .modes = { 242 + { FM_AUTO_NAME, 0x0d }, 243 + { FM_SILENT_NAME, 0x1d }, 244 + { FM_BASIC_NAME, 0x4d }, 245 + { FM_ADVANCED_NAME, 0x8d }, 246 + MSI_EC_MODE_NULL 247 + }, 248 + }, 249 + .cpu = { 250 + .rt_temp_address = 0x68, 251 + .rt_fan_speed_address = 0x71, 252 + .rt_fan_speed_base_min = 0x19, 253 + .rt_fan_speed_base_max = 0x37, 254 + .bs_fan_speed_address = 0x89, 255 + .bs_fan_speed_base_min = 0x00, 256 + .bs_fan_speed_base_max = 0x0f, 257 + }, 258 + .gpu = { 259 + .rt_temp_address = 0x80, 260 + .rt_fan_speed_address = 0x89, 261 + }, 262 + .leds = { 263 + .micmute_led_address = 0x2c, 264 + .mute_led_address = 0x2d, 265 + .bit = 1, 266 + }, 267 + .kbd_bl = { 268 + .bl_mode_address = 0x2c, // ? 269 + .bl_modes = { 0x00, 0x08 }, // ? 270 + .max_mode = 1, // ? 271 + .bl_state_address = 0xd3, 272 + .state_base_value = 0x80, 273 + .max_state = 3, 274 + }, 275 + }; 276 + 277 + static const char * const ALLOWED_FW_3[] __initconst = { 278 + "1592EMS1.111", 279 + "E1592IMS.10C", 280 + NULL 281 + }; 282 + 283 + static struct msi_ec_conf CONF3 __initdata = { 284 + .allowed_fw = ALLOWED_FW_3, 285 + .charge_control = { 286 + .address = 0xef, 287 + .offset_start = 0x8a, 288 + .offset_end = 0x80, 289 + .range_min = 0x8a, 290 + .range_max = 0xe4, 291 + }, 292 + .webcam = { 293 + .address = 0x2e, 294 + .block_address = 0x2f, 295 + .bit = 1, 296 + }, 297 + .fn_super_swap = { 298 + .address = 0xe8, 299 + .bit = 4, 300 + }, 301 + .cooler_boost = { 302 + .address = 0x98, 303 + .bit = 7, 304 + }, 305 + .shift_mode = { 306 + .address = 0xd2, 307 + .modes = { 308 + { SM_ECO_NAME, 0xc2 }, 309 + { SM_COMFORT_NAME, 0xc1 }, 310 + { SM_SPORT_NAME, 0xc0 }, 311 + MSI_EC_MODE_NULL 312 + }, 313 + }, 314 + .super_battery = { 315 + .address = 0xeb, 316 + .mask = 0x0f, 317 + }, 318 + .fan_mode = { 319 + .address = 0xd4, 320 + .modes = { 321 + { FM_AUTO_NAME, 0x0d }, 322 + { FM_SILENT_NAME, 0x1d }, 323 + { FM_BASIC_NAME, 0x4d }, 324 + { FM_ADVANCED_NAME, 0x8d }, 325 + MSI_EC_MODE_NULL 326 + }, 327 + }, 328 + .cpu = { 329 + .rt_temp_address = 0x68, 330 + .rt_fan_speed_address = 0xc9, 331 + .rt_fan_speed_base_min = 0x19, 332 + .rt_fan_speed_base_max = 0x37, 333 + .bs_fan_speed_address = 0x89, // ? 334 + .bs_fan_speed_base_min = 0x00, 335 + .bs_fan_speed_base_max = 0x0f, 336 + }, 337 + .gpu = { 338 + .rt_temp_address = 0x80, 339 + .rt_fan_speed_address = 0x89, 340 + }, 341 + .leds = { 342 + .micmute_led_address = 0x2b, 343 + .mute_led_address = 0x2c, 344 + .bit = 1, 345 + }, 346 + .kbd_bl = { 347 + .bl_mode_address = 0x2c, // ? 348 + .bl_modes = { 0x00, 0x08 }, // ? 349 + .max_mode = 1, // ? 350 + .bl_state_address = 0xd3, 351 + .state_base_value = 0x80, 352 + .max_state = 3, 353 + }, 354 + }; 355 + 356 + static const char * const ALLOWED_FW_4[] __initconst = { 357 + "16V4EMS1.114", 358 + NULL 359 + }; 360 + 361 + static struct msi_ec_conf CONF4 __initdata = { 362 + .allowed_fw = ALLOWED_FW_4, 363 + .charge_control = { 364 + .address = 0xd7, 365 + .offset_start = 0x8a, 366 + .offset_end = 0x80, 367 + .range_min = 0x8a, 368 + .range_max = 0xe4, 369 + }, 370 + .webcam = { 371 + .address = 0x2e, 372 + .block_address = 0x2f, 373 + .bit = 1, 374 + }, 375 + .fn_super_swap = { 376 + .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown 377 + .bit = 4, 378 + }, 379 + .cooler_boost = { 380 + .address = 0x98, 381 + .bit = 7, 382 + }, 383 + .shift_mode = { 384 + .address = 0xd2, 385 + .modes = { 386 + { SM_ECO_NAME, 0xc2 }, 387 + { SM_COMFORT_NAME, 0xc1 }, 388 + { SM_SPORT_NAME, 0xc0 }, 389 + MSI_EC_MODE_NULL 390 + }, 391 + }, 392 + .super_battery = { // may be supported, but address is unknown 393 + .address = MSI_EC_ADDR_UNKNOWN, 394 + .mask = 0x0f, 395 + }, 396 + .fan_mode = { 397 + .address = 0xd4, 398 + .modes = { 399 + { FM_AUTO_NAME, 0x0d }, 400 + { FM_SILENT_NAME, 0x1d }, 401 + { FM_ADVANCED_NAME, 0x8d }, 402 + MSI_EC_MODE_NULL 403 + }, 404 + }, 405 + .cpu = { 406 + .rt_temp_address = 0x68, // needs testing 407 + .rt_fan_speed_address = 0x71, // needs testing 408 + .rt_fan_speed_base_min = 0x19, 409 + .rt_fan_speed_base_max = 0x37, 410 + .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 411 + .bs_fan_speed_base_min = 0x00, 412 + .bs_fan_speed_base_max = 0x0f, 413 + }, 414 + .gpu = { 415 + .rt_temp_address = 0x80, 416 + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 417 + }, 418 + .leds = { 419 + .micmute_led_address = MSI_EC_ADDR_UNKNOWN, 420 + .mute_led_address = MSI_EC_ADDR_UNKNOWN, 421 + .bit = 1, 422 + }, 423 + .kbd_bl = { 424 + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 425 + .bl_modes = { 0x00, 0x08 }, // ? 426 + .max_mode = 1, // ? 427 + .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional 428 + .state_base_value = 0x80, 429 + .max_state = 3, 430 + }, 431 + }; 432 + 433 + static const char * const ALLOWED_FW_5[] __initconst = { 434 + "158LEMS1.103", 435 + "158LEMS1.105", 436 + "158LEMS1.106", 437 + NULL 438 + }; 439 + 440 + static struct msi_ec_conf CONF5 __initdata = { 441 + .allowed_fw = ALLOWED_FW_5, 442 + .charge_control = { 443 + .address = 0xef, 444 + .offset_start = 0x8a, 445 + .offset_end = 0x80, 446 + .range_min = 0x8a, 447 + .range_max = 0xe4, 448 + }, 449 + .webcam = { 450 + .address = 0x2e, 451 + .block_address = 0x2f, 452 + .bit = 1, 453 + }, 454 + .fn_super_swap = { // todo: reverse 455 + .address = 0xbf, 456 + .bit = 4, 457 + }, 458 + .cooler_boost = { 459 + .address = 0x98, 460 + .bit = 7, 461 + }, 462 + .shift_mode = { 463 + .address = 0xf2, 464 + .modes = { 465 + { SM_ECO_NAME, 0xc2 }, 466 + { SM_COMFORT_NAME, 0xc1 }, 467 + { SM_TURBO_NAME, 0xc4 }, 468 + MSI_EC_MODE_NULL 469 + }, 470 + }, 471 + .super_battery = { // unsupported? 472 + .address = MSI_EC_ADDR_UNKNOWN, 473 + .mask = 0x0f, 474 + }, 475 + .fan_mode = { 476 + .address = 0xf4, 477 + .modes = { 478 + { FM_AUTO_NAME, 0x0d }, 479 + { FM_SILENT_NAME, 0x1d }, 480 + { FM_ADVANCED_NAME, 0x8d }, 481 + MSI_EC_MODE_NULL 482 + }, 483 + }, 484 + .cpu = { 485 + .rt_temp_address = 0x68, // needs testing 486 + .rt_fan_speed_address = 0x71, // needs testing 487 + .rt_fan_speed_base_min = 0x19, 488 + .rt_fan_speed_base_max = 0x37, 489 + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 490 + .bs_fan_speed_base_min = 0x00, 491 + .bs_fan_speed_base_max = 0x0f, 492 + }, 493 + .gpu = { 494 + .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 495 + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 496 + }, 497 + .leds = { 498 + .micmute_led_address = 0x2b, 499 + .mute_led_address = 0x2c, 500 + .bit = 2, 501 + }, 502 + .kbd_bl = { 503 + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 504 + .bl_modes = { 0x00, 0x08 }, // ? 505 + .max_mode = 1, // ? 506 + .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 507 + .state_base_value = 0x80, 508 + .max_state = 3, 509 + }, 510 + }; 511 + 512 + static const char * const ALLOWED_FW_6[] __initconst = { 513 + "1542EMS1.102", 514 + "1542EMS1.104", 515 + NULL 516 + }; 517 + 518 + static struct msi_ec_conf CONF6 __initdata = { 519 + .allowed_fw = ALLOWED_FW_6, 520 + .charge_control = { 521 + .address = 0xef, 522 + .offset_start = 0x8a, 523 + .offset_end = 0x80, 524 + .range_min = 0x8a, 525 + .range_max = 0xe4, 526 + }, 527 + .webcam = { 528 + .address = 0x2e, 529 + .block_address = MSI_EC_ADDR_UNSUPP, 530 + .bit = 1, 531 + }, 532 + .fn_super_swap = { 533 + .address = 0xbf, // todo: reverse 534 + .bit = 4, 535 + }, 536 + .cooler_boost = { 537 + .address = 0x98, 538 + .bit = 7, 539 + }, 540 + .shift_mode = { 541 + .address = 0xf2, 542 + .modes = { 543 + { SM_ECO_NAME, 0xc2 }, 544 + { SM_COMFORT_NAME, 0xc1 }, 545 + { SM_SPORT_NAME, 0xc0 }, 546 + { SM_TURBO_NAME, 0xc4 }, 547 + MSI_EC_MODE_NULL 548 + }, 549 + }, 550 + .super_battery = { 551 + .address = 0xd5, 552 + .mask = 0x0f, 553 + }, 554 + .fan_mode = { 555 + .address = 0xf4, 556 + .modes = { 557 + { FM_AUTO_NAME, 0x0d }, 558 + { FM_SILENT_NAME, 0x1d }, 559 + { FM_ADVANCED_NAME, 0x8d }, 560 + MSI_EC_MODE_NULL 561 + }, 562 + }, 563 + .cpu = { 564 + .rt_temp_address = 0x68, 565 + .rt_fan_speed_address = 0xc9, 566 + .rt_fan_speed_base_min = 0x19, 567 + .rt_fan_speed_base_max = 0x37, 568 + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 569 + .bs_fan_speed_base_min = 0x00, 570 + .bs_fan_speed_base_max = 0x0f, 571 + }, 572 + .gpu = { 573 + .rt_temp_address = 0x80, 574 + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 575 + }, 576 + .leds = { 577 + .micmute_led_address = MSI_EC_ADDR_UNSUPP, 578 + .mute_led_address = MSI_EC_ADDR_UNSUPP, 579 + .bit = 2, 580 + }, 581 + .kbd_bl = { 582 + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 583 + .bl_modes = { 0x00, 0x08 }, // ? 584 + .max_mode = 1, // ? 585 + .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional 586 + .state_base_value = 0x80, 587 + .max_state = 3, 588 + }, 589 + }; 590 + 591 + static const char * const ALLOWED_FW_7[] __initconst = { 592 + "17FKEMS1.108", 593 + "17FKEMS1.109", 594 + "17FKEMS1.10A", 595 + NULL 596 + }; 597 + 598 + static struct msi_ec_conf CONF7 __initdata = { 599 + .allowed_fw = ALLOWED_FW_7, 600 + .charge_control = { 601 + .address = 0xef, 602 + .offset_start = 0x8a, 603 + .offset_end = 0x80, 604 + .range_min = 0x8a, 605 + .range_max = 0xe4, 606 + }, 607 + .webcam = { 608 + .address = 0x2e, 609 + .block_address = MSI_EC_ADDR_UNSUPP, 610 + .bit = 1, 611 + }, 612 + .fn_super_swap = { 613 + .address = 0xbf, // needs testing 614 + .bit = 4, 615 + }, 616 + .cooler_boost = { 617 + .address = 0x98, 618 + .bit = 7, 619 + }, 620 + .shift_mode = { 621 + .address = 0xf2, 622 + .modes = { 623 + { SM_ECO_NAME, 0xc2 }, 624 + { SM_COMFORT_NAME, 0xc1 }, 625 + { SM_SPORT_NAME, 0xc0 }, 626 + { SM_TURBO_NAME, 0xc4 }, 627 + MSI_EC_MODE_NULL 628 + }, 629 + }, 630 + .super_battery = { 631 + .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes 632 + .mask = 0x0f, 633 + }, 634 + .fan_mode = { 635 + .address = 0xf4, 636 + .modes = { 637 + { FM_AUTO_NAME, 0x0d }, // d may not be relevant 638 + { FM_SILENT_NAME, 0x1d }, 639 + { FM_ADVANCED_NAME, 0x8d }, 640 + MSI_EC_MODE_NULL 641 + }, 642 + }, 643 + .cpu = { 644 + .rt_temp_address = 0x68, 645 + .rt_fan_speed_address = 0xc9, // needs testing 646 + .rt_fan_speed_base_min = 0x19, 647 + .rt_fan_speed_base_max = 0x37, 648 + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, 649 + .bs_fan_speed_base_min = 0x00, 650 + .bs_fan_speed_base_max = 0x0f, 651 + }, 652 + .gpu = { 653 + .rt_temp_address = MSI_EC_ADDR_UNKNOWN, 654 + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, 655 + }, 656 + .leds = { 657 + .micmute_led_address = MSI_EC_ADDR_UNSUPP, 658 + .mute_led_address = 0x2c, 659 + .bit = 2, 660 + }, 661 + .kbd_bl = { 662 + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? 663 + .bl_modes = { 0x00, 0x08 }, // ? 664 + .max_mode = 1, // ? 665 + .bl_state_address = 0xf3, 666 + .state_base_value = 0x80, 667 + .max_state = 3, 668 + }, 669 + }; 670 + 671 + static struct msi_ec_conf *CONFIGS[] __initdata = { 672 + &CONF0, 673 + &CONF1, 674 + &CONF2, 675 + &CONF3, 676 + &CONF4, 677 + &CONF5, 678 + &CONF6, 679 + &CONF7, 680 + NULL 681 + }; 682 + 683 + static struct msi_ec_conf conf; // current configuration 684 + 685 + /* 686 + * Helper functions 687 + */ 688 + 689 + static int ec_read_seq(u8 addr, u8 *buf, u8 len) 690 + { 691 + int result; 692 + 693 + for (u8 i = 0; i < len; i++) { 694 + result = ec_read(addr + i, buf + i); 695 + if (result < 0) 696 + return result; 697 + } 698 + 699 + return 0; 700 + } 701 + 702 + static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) 703 + { 704 + int result; 705 + 706 + memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); 707 + result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, 708 + buf, 709 + MSI_EC_FW_VERSION_LENGTH); 710 + if (result < 0) 711 + return result; 712 + 713 + return MSI_EC_FW_VERSION_LENGTH + 1; 714 + } 715 + 716 + /* 717 + * Sysfs power_supply subsystem 718 + */ 719 + 720 + static ssize_t charge_control_threshold_show(u8 offset, 721 + struct device *device, 722 + struct device_attribute *attr, 723 + char *buf) 724 + { 725 + u8 rdata; 726 + int result; 727 + 728 + result = ec_read(conf.charge_control.address, &rdata); 729 + if (result < 0) 730 + return result; 731 + 732 + return sysfs_emit(buf, "%i\n", rdata - offset); 733 + } 734 + 735 + static ssize_t charge_control_threshold_store(u8 offset, 736 + struct device *dev, 737 + struct device_attribute *attr, 738 + const char *buf, size_t count) 739 + { 740 + u8 wdata; 741 + int result; 742 + 743 + result = kstrtou8(buf, 10, &wdata); 744 + if (result < 0) 745 + return result; 746 + 747 + wdata += offset; 748 + if (wdata < conf.charge_control.range_min || 749 + wdata > conf.charge_control.range_max) 750 + return -EINVAL; 751 + 752 + result = ec_write(conf.charge_control.address, wdata); 753 + if (result < 0) 754 + return result; 755 + 756 + return count; 757 + } 758 + 759 + static ssize_t charge_control_start_threshold_show(struct device *device, 760 + struct device_attribute *attr, 761 + char *buf) 762 + { 763 + return charge_control_threshold_show(conf.charge_control.offset_start, 764 + device, attr, buf); 765 + } 766 + 767 + static ssize_t charge_control_start_threshold_store(struct device *dev, 768 + struct device_attribute *attr, 769 + const char *buf, size_t count) 770 + { 771 + return charge_control_threshold_store(conf.charge_control.offset_start, 772 + dev, attr, buf, count); 773 + } 774 + 775 + static ssize_t charge_control_end_threshold_show(struct device *device, 776 + struct device_attribute *attr, 777 + char *buf) 778 + { 779 + return charge_control_threshold_show(conf.charge_control.offset_end, 780 + device, attr, buf); 781 + } 782 + 783 + static ssize_t charge_control_end_threshold_store(struct device *dev, 784 + struct device_attribute *attr, 785 + const char *buf, size_t count) 786 + { 787 + return charge_control_threshold_store(conf.charge_control.offset_end, 788 + dev, attr, buf, count); 789 + } 790 + 791 + static DEVICE_ATTR_RW(charge_control_start_threshold); 792 + static DEVICE_ATTR_RW(charge_control_end_threshold); 793 + 794 + static struct attribute *msi_battery_attrs[] = { 795 + &dev_attr_charge_control_start_threshold.attr, 796 + &dev_attr_charge_control_end_threshold.attr, 797 + NULL 798 + }; 799 + 800 + ATTRIBUTE_GROUPS(msi_battery); 801 + 802 + static int msi_battery_add(struct power_supply *battery, 803 + struct acpi_battery_hook *hook) 804 + { 805 + return device_add_groups(&battery->dev, msi_battery_groups); 806 + } 807 + 808 + static int msi_battery_remove(struct power_supply *battery, 809 + struct acpi_battery_hook *hook) 810 + { 811 + device_remove_groups(&battery->dev, msi_battery_groups); 812 + return 0; 813 + } 814 + 815 + static struct acpi_battery_hook battery_hook = { 816 + .add_battery = msi_battery_add, 817 + .remove_battery = msi_battery_remove, 818 + .name = MSI_EC_DRIVER_NAME, 819 + }; 820 + 821 + /* 822 + * Module load/unload 823 + */ 824 + 825 + static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { 826 + { 827 + .matches = { 828 + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), 829 + }, 830 + }, 831 + { 832 + .matches = { 833 + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 834 + }, 835 + }, 836 + {} 837 + }; 838 + MODULE_DEVICE_TABLE(dmi, msi_dmi_table); 839 + 840 + static int __init load_configuration(void) 841 + { 842 + int result; 843 + 844 + u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; 845 + 846 + /* get firmware version */ 847 + result = ec_get_firmware_version(fw_version); 848 + if (result < 0) 849 + return result; 850 + 851 + /* load the suitable configuration, if exists */ 852 + for (int i = 0; CONFIGS[i]; i++) { 853 + if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { 854 + conf = *CONFIGS[i]; 855 + conf.allowed_fw = NULL; 856 + return 0; 857 + } 858 + } 859 + 860 + /* config not found */ 861 + 862 + for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { 863 + if (!isgraph(fw_version[i])) { 864 + pr_warn("Unable to find a valid firmware version!\n"); 865 + return -EOPNOTSUPP; 866 + } 867 + } 868 + 869 + pr_warn("Firmware version is not supported: '%s'\n", fw_version); 870 + return -EOPNOTSUPP; 871 + } 872 + 873 + static int __init msi_ec_init(void) 874 + { 875 + int result; 876 + 877 + result = load_configuration(); 878 + if (result < 0) 879 + return result; 880 + 881 + battery_hook_register(&battery_hook); 882 + return 0; 883 + } 884 + 885 + static void __exit msi_ec_exit(void) 886 + { 887 + battery_hook_unregister(&battery_hook); 888 + } 889 + 890 + MODULE_LICENSE("GPL"); 891 + MODULE_AUTHOR("Jose Angel Pastrana <japp0005@red.ujaen.es>"); 892 + MODULE_AUTHOR("Aakash Singh <mail@singhaakash.dev>"); 893 + MODULE_AUTHOR("Nikita Kravets <teackot@gmail.com>"); 894 + MODULE_DESCRIPTION("MSI Embedded Controller"); 895 + 896 + module_init(msi_ec_init); 897 + module_exit(msi_ec_exit);
+122
drivers/platform/x86/msi-ec.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* 4 + * msi-ec: MSI laptops' embedded controller driver. 5 + * 6 + * Copyright (C) 2023 Jose Angel Pastrana <japp0005@red.ujaen.es> 7 + * Copyright (C) 2023 Aakash Singh <mail@singhaakash.dev> 8 + * Copyright (C) 2023 Nikita Kravets <teackot@gmail.com> 9 + */ 10 + 11 + #ifndef _MSI_EC_H_ 12 + #define _MSI_EC_H_ 13 + 14 + #include <linux/types.h> 15 + 16 + #define MSI_EC_DRIVER_NAME "msi-ec" 17 + 18 + #define MSI_EC_ADDR_UNKNOWN 0xff01 // unknown address 19 + #define MSI_EC_ADDR_UNSUPP 0xff01 // unsupported parameter 20 + 21 + // Firmware info addresses are universal 22 + #define MSI_EC_FW_VERSION_ADDRESS 0xa0 23 + #define MSI_EC_FW_DATE_ADDRESS 0xac 24 + #define MSI_EC_FW_TIME_ADDRESS 0xb4 25 + #define MSI_EC_FW_VERSION_LENGTH 12 26 + #define MSI_EC_FW_DATE_LENGTH 8 27 + #define MSI_EC_FW_TIME_LENGTH 8 28 + 29 + struct msi_ec_charge_control_conf { 30 + int address; 31 + int offset_start; 32 + int offset_end; 33 + int range_min; 34 + int range_max; 35 + }; 36 + 37 + struct msi_ec_webcam_conf { 38 + int address; 39 + int block_address; 40 + int bit; 41 + }; 42 + 43 + struct msi_ec_fn_super_swap_conf { 44 + int address; 45 + int bit; 46 + }; 47 + 48 + struct msi_ec_cooler_boost_conf { 49 + int address; 50 + int bit; 51 + }; 52 + 53 + #define MSI_EC_MODE_NULL { NULL, 0 } 54 + struct msi_ec_mode { 55 + const char *name; 56 + int value; 57 + }; 58 + 59 + struct msi_ec_shift_mode_conf { 60 + int address; 61 + struct msi_ec_mode modes[5]; // fixed size for easier hard coding 62 + }; 63 + 64 + struct msi_ec_super_battery_conf { 65 + int address; 66 + int mask; 67 + }; 68 + 69 + struct msi_ec_fan_mode_conf { 70 + int address; 71 + struct msi_ec_mode modes[5]; // fixed size for easier hard coding 72 + }; 73 + 74 + struct msi_ec_cpu_conf { 75 + int rt_temp_address; 76 + int rt_fan_speed_address; // realtime 77 + int rt_fan_speed_base_min; 78 + int rt_fan_speed_base_max; 79 + int bs_fan_speed_address; // basic 80 + int bs_fan_speed_base_min; 81 + int bs_fan_speed_base_max; 82 + }; 83 + 84 + struct msi_ec_gpu_conf { 85 + int rt_temp_address; 86 + int rt_fan_speed_address; // realtime 87 + }; 88 + 89 + struct msi_ec_led_conf { 90 + int micmute_led_address; 91 + int mute_led_address; 92 + int bit; 93 + }; 94 + 95 + #define MSI_EC_KBD_BL_STATE_MASK 0x3 96 + struct msi_ec_kbd_bl_conf { 97 + int bl_mode_address; 98 + int bl_modes[2]; 99 + int max_mode; 100 + 101 + int bl_state_address; 102 + int state_base_value; 103 + int max_state; 104 + }; 105 + 106 + struct msi_ec_conf { 107 + const char * const *allowed_fw; 108 + 109 + struct msi_ec_charge_control_conf charge_control; 110 + struct msi_ec_webcam_conf webcam; 111 + struct msi_ec_fn_super_swap_conf fn_super_swap; 112 + struct msi_ec_cooler_boost_conf cooler_boost; 113 + struct msi_ec_shift_mode_conf shift_mode; 114 + struct msi_ec_super_battery_conf super_battery; 115 + struct msi_ec_fan_mode_conf fan_mode; 116 + struct msi_ec_cpu_conf cpu; 117 + struct msi_ec_gpu_conf gpu; 118 + struct msi_ec_led_conf leds; 119 + struct msi_ec_kbd_bl_conf kbd_bl; 120 + }; 121 + 122 + #endif // _MSI_EC_H_
-1
drivers/platform/x86/pcengines-apuv2.c
··· 291 291 MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver"); 292 292 MODULE_LICENSE("GPL"); 293 293 MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); 294 - MODULE_ALIAS("platform:pcengines-apuv2"); 295 294 MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");
-128
drivers/platform/x86/peaq-wmi.c
··· 1 - // SPDX-License-Identifier: GPL-2.0-only 2 - /* 3 - * PEAQ 2-in-1 WMI hotkey driver 4 - * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> 5 - */ 6 - 7 - #include <linux/acpi.h> 8 - #include <linux/dmi.h> 9 - #include <linux/input.h> 10 - #include <linux/kernel.h> 11 - #include <linux/module.h> 12 - 13 - #define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000" 14 - #define PEAQ_DOLBY_BUTTON_METHOD_ID 5 15 - #define PEAQ_POLL_INTERVAL_MS 250 16 - #define PEAQ_POLL_IGNORE_MS 500 17 - #define PEAQ_POLL_MAX_MS 1000 18 - 19 - MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID); 20 - 21 - static struct input_dev *peaq_poll_dev; 22 - 23 - /* 24 - * The Dolby button (yes really a Dolby button) causes an ACPI variable to get 25 - * set on both press and release. The WMI method checks and clears that flag. 26 - * So for a press + release we will get back One from the WMI method either once 27 - * (if polling after the release) or twice (polling between press and release). 28 - * We ignore events for 0.5s after the first event to avoid reporting 2 presses. 29 - */ 30 - static void peaq_wmi_poll(struct input_dev *input_dev) 31 - { 32 - static unsigned long last_event_time; 33 - static bool had_events; 34 - union acpi_object obj; 35 - acpi_status status; 36 - u32 dummy = 0; 37 - 38 - struct acpi_buffer input = { sizeof(dummy), &dummy }; 39 - struct acpi_buffer output = { sizeof(obj), &obj }; 40 - 41 - status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 0, 42 - PEAQ_DOLBY_BUTTON_METHOD_ID, 43 - &input, &output); 44 - if (ACPI_FAILURE(status)) 45 - return; 46 - 47 - if (obj.type != ACPI_TYPE_INTEGER) { 48 - dev_err(&input_dev->dev, 49 - "Error WMBC did not return an integer\n"); 50 - return; 51 - } 52 - 53 - if (!obj.integer.value) 54 - return; 55 - 56 - if (had_events && time_before(jiffies, last_event_time + 57 - msecs_to_jiffies(PEAQ_POLL_IGNORE_MS))) 58 - return; 59 - 60 - input_event(input_dev, EV_KEY, KEY_SOUND, 1); 61 - input_sync(input_dev); 62 - input_event(input_dev, EV_KEY, KEY_SOUND, 0); 63 - input_sync(input_dev); 64 - 65 - last_event_time = jiffies; 66 - had_events = true; 67 - } 68 - 69 - /* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ 70 - static const struct dmi_system_id peaq_dmi_table[] __initconst = { 71 - { 72 - .matches = { 73 - DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), 74 - DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), 75 - }, 76 - }, 77 - {} 78 - }; 79 - 80 - static int __init peaq_wmi_init(void) 81 - { 82 - int err; 83 - 84 - /* WMI GUID is not unique, also check for a DMI match */ 85 - if (!dmi_check_system(peaq_dmi_table)) 86 - return -ENODEV; 87 - 88 - if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) 89 - return -ENODEV; 90 - 91 - peaq_poll_dev = input_allocate_device(); 92 - if (!peaq_poll_dev) 93 - return -ENOMEM; 94 - 95 - peaq_poll_dev->name = "PEAQ WMI hotkeys"; 96 - peaq_poll_dev->phys = "wmi/input0"; 97 - peaq_poll_dev->id.bustype = BUS_HOST; 98 - input_set_capability(peaq_poll_dev, EV_KEY, KEY_SOUND); 99 - 100 - err = input_setup_polling(peaq_poll_dev, peaq_wmi_poll); 101 - if (err) 102 - goto err_out; 103 - 104 - input_set_poll_interval(peaq_poll_dev, PEAQ_POLL_INTERVAL_MS); 105 - input_set_max_poll_interval(peaq_poll_dev, PEAQ_POLL_MAX_MS); 106 - 107 - err = input_register_device(peaq_poll_dev); 108 - if (err) 109 - goto err_out; 110 - 111 - return 0; 112 - 113 - err_out: 114 - input_free_device(peaq_poll_dev); 115 - return err; 116 - } 117 - 118 - static void __exit peaq_wmi_exit(void) 119 - { 120 - input_unregister_device(peaq_poll_dev); 121 - } 122 - 123 - module_init(peaq_wmi_init); 124 - module_exit(peaq_wmi_exit); 125 - 126 - MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver"); 127 - MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 128 - MODULE_LICENSE("GPL");
+2 -4
drivers/platform/x86/samsung-q10.c
··· 65 65 return 0; 66 66 } 67 67 68 - static int samsungq10_remove(struct platform_device *pdev) 68 + static void samsungq10_remove(struct platform_device *pdev) 69 69 { 70 70 71 71 struct backlight_device *bd = platform_get_drvdata(pdev); 72 72 73 73 backlight_device_unregister(bd); 74 - 75 - return 0; 76 74 } 77 75 78 76 static struct platform_driver samsungq10_driver = { ··· 78 80 .name = KBUILD_MODNAME, 79 81 }, 80 82 .probe = samsungq10_probe, 81 - .remove = samsungq10_remove, 83 + .remove_new = samsungq10_remove, 82 84 }; 83 85 84 86 static struct platform_device *samsungq10_device;
+2 -4
drivers/platform/x86/serial-multi-instantiate.c
··· 265 265 } 266 266 } 267 267 268 - static int smi_remove(struct platform_device *pdev) 268 + static void smi_remove(struct platform_device *pdev) 269 269 { 270 270 struct smi *smi = platform_get_drvdata(pdev); 271 271 272 272 smi_devs_unregister(smi); 273 - 274 - return 0; 275 273 } 276 274 277 275 static const struct smi_node bsg1160_data = { ··· 337 339 .acpi_match_table = smi_acpi_ids, 338 340 }, 339 341 .probe = smi_probe, 340 - .remove = smi_remove, 342 + .remove_new = smi_remove, 341 343 }; 342 344 module_platform_driver(smi_driver); 343 345
+1 -1
drivers/platform/x86/sony-laptop.c
··· 3287 3287 dprintk(SONY_NC_DRIVER_NAME " removed.\n"); 3288 3288 } 3289 3289 3290 - static const struct acpi_device_id sony_device_ids[] = { 3290 + static const struct acpi_device_id sony_device_ids[] __maybe_unused = { 3291 3291 {SONY_NC_HID, 0}, 3292 3292 {SONY_PIC_HID, 0}, 3293 3293 {"", 0},
+10 -39
drivers/platform/x86/think-lmi.c
··· 862 862 struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 863 863 864 864 /* We only want to display level and index settings on HDD/NVMe */ 865 - if ((attr == (struct attribute *)&auth_index) || 866 - (attr == (struct attribute *)&auth_level)) { 865 + if (attr == &auth_index.attr || attr == &auth_level.attr) { 867 866 if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme)) 868 867 return attr->mode; 869 868 return 0; 870 869 } 871 870 872 871 /* We only display certificates on Admin account, if supported */ 873 - if ((attr == (struct attribute *)&auth_certificate) || 874 - (attr == (struct attribute *)&auth_signature) || 875 - (attr == (struct attribute *)&auth_save_signature) || 876 - (attr == (struct attribute *)&auth_cert_thumb) || 877 - (attr == (struct attribute *)&auth_cert_to_password)) { 872 + if (attr == &auth_certificate.attr || 873 + attr == &auth_signature.attr || 874 + attr == &auth_save_signature.attr || 875 + attr == &auth_cert_thumb.attr || 876 + attr == &auth_cert_to_password.attr) { 878 877 if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support) 879 878 return attr->mode; 880 879 return 0; ··· 1078 1079 .attrs = tlmi_attrs, 1079 1080 }; 1080 1081 1081 - static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr, 1082 - char *buf) 1083 - { 1084 - struct kobj_attribute *kattr; 1085 - 1086 - kattr = container_of(attr, struct kobj_attribute, attr); 1087 - if (kattr->show) 1088 - return kattr->show(kobj, kattr, buf); 1089 - return -EIO; 1090 - } 1091 - 1092 - static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr, 1093 - const char *buf, size_t count) 1094 - { 1095 - struct kobj_attribute *kattr; 1096 - 1097 - kattr = container_of(attr, struct kobj_attribute, attr); 1098 - if (kattr->store) 1099 - return kattr->store(kobj, kattr, buf, count); 1100 - return -EIO; 1101 - } 1102 - 1103 - static const struct sysfs_ops tlmi_kobj_sysfs_ops = { 1104 - .show = tlmi_attr_show, 1105 - .store = tlmi_attr_store, 1106 - }; 1107 - 1108 1082 static void tlmi_attr_setting_release(struct kobject *kobj) 1109 1083 { 1110 1084 struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); ··· 1095 1123 1096 1124 static const struct kobj_type tlmi_attr_setting_ktype = { 1097 1125 .release = &tlmi_attr_setting_release, 1098 - .sysfs_ops = &tlmi_kobj_sysfs_ops, 1126 + .sysfs_ops = &kobj_sysfs_ops, 1099 1127 }; 1100 1128 1101 1129 static const struct kobj_type tlmi_pwd_setting_ktype = { 1102 1130 .release = &tlmi_pwd_setting_release, 1103 - .sysfs_ops = &tlmi_kobj_sysfs_ops, 1131 + .sysfs_ops = &kobj_sysfs_ops, 1104 1132 }; 1105 1133 1106 1134 static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, ··· 1357 1385 1358 1386 static int tlmi_analyze(void) 1359 1387 { 1360 - acpi_status status; 1361 1388 int i, ret; 1362 1389 1363 1390 if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && ··· 1393 1422 char *p; 1394 1423 1395 1424 tlmi_priv.setting[i] = NULL; 1396 - status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); 1397 - if (ACPI_FAILURE(status)) 1425 + ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); 1426 + if (ret) 1398 1427 break; 1399 1428 if (!item) 1400 1429 break;
+16
drivers/platform/x86/thinkpad_acpi.c
··· 11699 11699 { 11700 11700 const struct dmi_system_id *dmi_id; 11701 11701 int ret, i; 11702 + acpi_object_type obj_type; 11702 11703 11703 11704 tpacpi_lifecycle = TPACPI_LIFE_INIT; 11704 11705 ··· 11724 11723 11725 11724 TPACPI_ACPIHANDLE_INIT(ecrd); 11726 11725 TPACPI_ACPIHANDLE_INIT(ecwr); 11726 + 11727 + /* 11728 + * Quirk: in some models (e.g. X380 Yoga), an object named ECRD 11729 + * exists, but it is a register, not a method. 11730 + */ 11731 + if (ecrd_handle) { 11732 + acpi_get_type(ecrd_handle, &obj_type); 11733 + if (obj_type != ACPI_TYPE_METHOD) 11734 + ecrd_handle = NULL; 11735 + } 11736 + if (ecwr_handle) { 11737 + acpi_get_type(ecwr_handle, &obj_type); 11738 + if (obj_type != ACPI_TYPE_METHOD) 11739 + ecwr_handle = NULL; 11740 + } 11727 11741 11728 11742 tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME); 11729 11743 if (!tpacpi_wq) {
+2 -4
drivers/platform/x86/wmi.c
··· 1369 1369 event, 0); 1370 1370 } 1371 1371 1372 - static int acpi_wmi_remove(struct platform_device *device) 1372 + static void acpi_wmi_remove(struct platform_device *device) 1373 1373 { 1374 1374 struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); 1375 1375 ··· 1379 1379 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 1380 1380 wmi_free_devices(acpi_device); 1381 1381 device_unregister(dev_get_drvdata(&device->dev)); 1382 - 1383 - return 0; 1384 1382 } 1385 1383 1386 1384 static int acpi_wmi_probe(struct platform_device *device) ··· 1466 1468 .acpi_match_table = wmi_device_ids, 1467 1469 }, 1468 1470 .probe = acpi_wmi_probe, 1469 - .remove = acpi_wmi_remove, 1471 + .remove_new = acpi_wmi_remove, 1470 1472 }; 1471 1473 1472 1474 static int __init acpi_wmi_init(void)
-1803
drivers/platform/x86/x86-android-tablets.c
··· 1 - // SPDX-License-Identifier: GPL-2.0+ 2 - /* 3 - * DMI based code to deal with broken DSDTs on X86 tablets which ship with 4 - * Android as (part of) the factory image. The factory kernels shipped on these 5 - * devices typically have a bunch of things hardcoded, rather than specified 6 - * in their DSDT. 7 - * 8 - * Copyright (C) 2021-2022 Hans de Goede <hdegoede@redhat.com> 9 - */ 10 - 11 - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 - 13 - #include <linux/acpi.h> 14 - #include <linux/dmi.h> 15 - #include <linux/efi.h> 16 - #include <linux/gpio_keys.h> 17 - #include <linux/gpio/consumer.h> 18 - #include <linux/gpio/driver.h> 19 - #include <linux/gpio/machine.h> 20 - #include <linux/i2c.h> 21 - #include <linux/input.h> 22 - #include <linux/irq.h> 23 - #include <linux/irqdomain.h> 24 - #include <linux/module.h> 25 - #include <linux/mod_devicetable.h> 26 - #include <linux/pinctrl/consumer.h> 27 - #include <linux/pinctrl/machine.h> 28 - #include <linux/platform_data/lp855x.h> 29 - #include <linux/platform_device.h> 30 - #include <linux/power/bq24190_charger.h> 31 - #include <linux/reboot.h> 32 - #include <linux/rmi.h> 33 - #include <linux/serdev.h> 34 - #include <linux/spi/spi.h> 35 - #include <linux/string.h> 36 - /* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */ 37 - #include "../../gpio/gpiolib.h" 38 - #include "../../gpio/gpiolib-acpi.h" 39 - 40 - /* 41 - * Helper code to get Linux IRQ numbers given a description of the IRQ source 42 - * (either IOAPIC index, or GPIO chip name + pin-number). 43 - */ 44 - enum x86_acpi_irq_type { 45 - X86_ACPI_IRQ_TYPE_NONE, 46 - X86_ACPI_IRQ_TYPE_APIC, 47 - X86_ACPI_IRQ_TYPE_GPIOINT, 48 - X86_ACPI_IRQ_TYPE_PMIC, 49 - }; 50 - 51 - struct x86_acpi_irq_data { 52 - char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */ 53 - enum x86_acpi_irq_type type; 54 - enum irq_domain_bus_token domain; 55 - int index; 56 - int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */ 57 - int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */ 58 - }; 59 - 60 - static int gpiochip_find_match_label(struct gpio_chip *gc, void *data) 61 - { 62 - return gc->label && !strcmp(gc->label, data); 63 - } 64 - 65 - static int x86_android_tablet_get_gpiod(char *label, int pin, struct gpio_desc **desc) 66 - { 67 - struct gpio_desc *gpiod; 68 - struct gpio_chip *chip; 69 - 70 - chip = gpiochip_find(label, gpiochip_find_match_label); 71 - if (!chip) { 72 - pr_err("error cannot find GPIO chip %s\n", label); 73 - return -ENODEV; 74 - } 75 - 76 - gpiod = gpiochip_get_desc(chip, pin); 77 - if (IS_ERR(gpiod)) { 78 - pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin); 79 - return PTR_ERR(gpiod); 80 - } 81 - 82 - *desc = gpiod; 83 - return 0; 84 - } 85 - 86 - static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) 87 - { 88 - struct irq_fwspec fwspec = { }; 89 - struct irq_domain *domain; 90 - struct acpi_device *adev; 91 - struct gpio_desc *gpiod; 92 - unsigned int irq_type; 93 - acpi_handle handle; 94 - acpi_status status; 95 - int irq, ret; 96 - 97 - switch (data->type) { 98 - case X86_ACPI_IRQ_TYPE_APIC: 99 - /* 100 - * The DSDT may already reference the GSI in a device skipped by 101 - * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI 102 - * to avoid EBUSY errors in this case. 103 - */ 104 - acpi_unregister_gsi(data->index); 105 - irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); 106 - if (irq < 0) 107 - pr_err("error %d getting APIC IRQ %d\n", irq, data->index); 108 - 109 - return irq; 110 - case X86_ACPI_IRQ_TYPE_GPIOINT: 111 - /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ 112 - ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod); 113 - if (ret) 114 - return ret; 115 - 116 - irq = gpiod_to_irq(gpiod); 117 - if (irq < 0) { 118 - pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); 119 - return irq; 120 - } 121 - 122 - irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); 123 - if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) 124 - irq_set_irq_type(irq, irq_type); 125 - 126 - return irq; 127 - case X86_ACPI_IRQ_TYPE_PMIC: 128 - status = acpi_get_handle(NULL, data->chip, &handle); 129 - if (ACPI_FAILURE(status)) { 130 - pr_err("error could not get %s handle\n", data->chip); 131 - return -ENODEV; 132 - } 133 - 134 - adev = acpi_fetch_acpi_dev(handle); 135 - if (!adev) { 136 - pr_err("error could not get %s adev\n", data->chip); 137 - return -ENODEV; 138 - } 139 - 140 - fwspec.fwnode = acpi_fwnode_handle(adev); 141 - domain = irq_find_matching_fwspec(&fwspec, data->domain); 142 - if (!domain) { 143 - pr_err("error could not find IRQ domain for %s\n", data->chip); 144 - return -ENODEV; 145 - } 146 - 147 - return irq_create_mapping(domain, data->index); 148 - default: 149 - return 0; 150 - } 151 - } 152 - 153 - struct x86_i2c_client_info { 154 - struct i2c_board_info board_info; 155 - char *adapter_path; 156 - struct x86_acpi_irq_data irq_data; 157 - }; 158 - 159 - struct x86_serdev_info { 160 - const char *ctrl_hid; 161 - const char *ctrl_uid; 162 - const char *ctrl_devname; 163 - /* 164 - * ATM the serdev core only supports of or ACPI matching; and sofar all 165 - * Android x86 tablets DSDTs have usable serdev nodes, but sometimes 166 - * under the wrong controller. So we just tie the existing serdev ACPI 167 - * node to the right controller. 168 - */ 169 - const char *serdev_hid; 170 - }; 171 - 172 - struct x86_dev_info { 173 - char *invalid_aei_gpiochip; 174 - const char * const *modules; 175 - const struct software_node *bat_swnode; 176 - struct gpiod_lookup_table * const *gpiod_lookup_tables; 177 - const struct x86_i2c_client_info *i2c_client_info; 178 - const struct platform_device_info *pdev_info; 179 - const struct x86_serdev_info *serdev_info; 180 - int i2c_client_count; 181 - int pdev_count; 182 - int serdev_count; 183 - int (*init)(void); 184 - void (*exit)(void); 185 - }; 186 - 187 - /* Generic / shared charger / battery settings */ 188 - static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; 189 - static const char * const bq24190_psy[] = { "bq24190-charger" }; 190 - static const char * const bq25890_psy[] = { "bq25890-charger-0" }; 191 - 192 - static const struct property_entry fg_bq24190_supply_props[] = { 193 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), 194 - { } 195 - }; 196 - 197 - static const struct software_node fg_bq24190_supply_node = { 198 - .properties = fg_bq24190_supply_props, 199 - }; 200 - 201 - static const struct property_entry fg_bq25890_supply_props[] = { 202 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy), 203 - { } 204 - }; 205 - 206 - static const struct software_node fg_bq25890_supply_node = { 207 - .properties = fg_bq25890_supply_props, 208 - }; 209 - 210 - /* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ 211 - static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { 212 - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), 213 - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), 214 - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), 215 - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), 216 - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), 217 - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), 218 - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 219 - { } 220 - }; 221 - 222 - static const struct software_node generic_lipo_hv_4v35_battery_node = { 223 - .properties = generic_lipo_hv_4v35_battery_props, 224 - }; 225 - 226 - /* For enabling the bq24190 5V boost based on id-pin */ 227 - static struct regulator_consumer_supply intel_int3496_consumer = { 228 - .supply = "vbus", 229 - .dev_name = "intel-int3496", 230 - }; 231 - 232 - static const struct regulator_init_data bq24190_vbus_init_data = { 233 - .constraints = { 234 - .name = "bq24190_vbus", 235 - .valid_ops_mask = REGULATOR_CHANGE_STATUS, 236 - }, 237 - .consumer_supplies = &intel_int3496_consumer, 238 - .num_consumer_supplies = 1, 239 - }; 240 - 241 - static struct bq24190_platform_data bq24190_pdata = { 242 - .regulator_init_data = &bq24190_vbus_init_data, 243 - }; 244 - 245 - static const char * const bq24190_modules[] __initconst = { 246 - "intel_crystal_cove_charger", /* For the bq24190 IRQ */ 247 - "bq24190_charger", /* For the Vbus regulator for intel-int3496 */ 248 - NULL 249 - }; 250 - 251 - /* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ 252 - static const struct platform_device_info int3496_pdevs[] __initconst = { 253 - { 254 - /* For micro USB ID pin handling */ 255 - .name = "intel-int3496", 256 - .id = PLATFORM_DEVID_NONE, 257 - }, 258 - }; 259 - 260 - static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { 261 - .dev_id = "intel-int3496", 262 - .table = { 263 - GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH), 264 - { } 265 - }, 266 - }; 267 - 268 - /* 269 - * Advantech MICA-071 270 - * This is a standard Windows tablet, but it has an extra "quick launch" button 271 - * which is not described in the ACPI tables in anyway. 272 - * Use the x86-android-tablets infra to create a gpio-button device for this. 273 - */ 274 - static struct gpio_keys_button advantech_mica_071_button = { 275 - .code = KEY_PROG1, 276 - /* .gpio gets filled in by advantech_mica_071_init() */ 277 - .active_low = true, 278 - .desc = "prog1_key", 279 - .type = EV_KEY, 280 - .wakeup = false, 281 - .debounce_interval = 50, 282 - }; 283 - 284 - static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { 285 - .buttons = &advantech_mica_071_button, 286 - .nbuttons = 1, 287 - .name = "prog1_key", 288 - }; 289 - 290 - static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { 291 - { 292 - .name = "gpio-keys", 293 - .id = PLATFORM_DEVID_AUTO, 294 - .data = &advantech_mica_071_button_pdata, 295 - .size_data = sizeof(advantech_mica_071_button_pdata), 296 - }, 297 - }; 298 - 299 - static int __init advantech_mica_071_init(void) 300 - { 301 - struct gpio_desc *gpiod; 302 - int ret; 303 - 304 - ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); 305 - if (ret < 0) 306 - return ret; 307 - advantech_mica_071_button.gpio = desc_to_gpio(gpiod); 308 - 309 - return 0; 310 - } 311 - 312 - static const struct x86_dev_info advantech_mica_071_info __initconst = { 313 - .pdev_info = advantech_mica_071_pdevs, 314 - .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), 315 - .init = advantech_mica_071_init, 316 - }; 317 - 318 - /* Asus ME176C and TF103C tablets shared data */ 319 - static struct gpio_keys_button asus_me176c_tf103c_lid = { 320 - .code = SW_LID, 321 - /* .gpio gets filled in by asus_me176c_tf103c_init() */ 322 - .active_low = true, 323 - .desc = "lid_sw", 324 - .type = EV_SW, 325 - .wakeup = true, 326 - .debounce_interval = 50, 327 - }; 328 - 329 - static const struct gpio_keys_platform_data asus_me176c_tf103c_lid_pdata __initconst = { 330 - .buttons = &asus_me176c_tf103c_lid, 331 - .nbuttons = 1, 332 - .name = "lid_sw", 333 - }; 334 - 335 - static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = { 336 - { 337 - .name = "gpio-keys", 338 - .id = PLATFORM_DEVID_AUTO, 339 - .data = &asus_me176c_tf103c_lid_pdata, 340 - .size_data = sizeof(asus_me176c_tf103c_lid_pdata), 341 - }, 342 - { 343 - /* For micro USB ID pin handling */ 344 - .name = "intel-int3496", 345 - .id = PLATFORM_DEVID_NONE, 346 - }, 347 - }; 348 - 349 - static int __init asus_me176c_tf103c_init(void) 350 - { 351 - struct gpio_desc *gpiod; 352 - int ret; 353 - 354 - ret = x86_android_tablet_get_gpiod("INT33FC:02", 12, &gpiod); 355 - if (ret < 0) 356 - return ret; 357 - asus_me176c_tf103c_lid.gpio = desc_to_gpio(gpiod); 358 - 359 - return 0; 360 - } 361 - 362 - 363 - /* Asus ME176C tablets have an Android factory img with everything hardcoded */ 364 - static const char * const asus_me176c_accel_mount_matrix[] = { 365 - "-1", "0", "0", 366 - "0", "1", "0", 367 - "0", "0", "1" 368 - }; 369 - 370 - static const struct property_entry asus_me176c_accel_props[] = { 371 - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix), 372 - { } 373 - }; 374 - 375 - static const struct software_node asus_me176c_accel_node = { 376 - .properties = asus_me176c_accel_props, 377 - }; 378 - 379 - static const struct property_entry asus_me176c_bq24190_props[] = { 380 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), 381 - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 382 - PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), 383 - PROPERTY_ENTRY_BOOL("omit-battery-class"), 384 - PROPERTY_ENTRY_BOOL("disable-reset"), 385 - { } 386 - }; 387 - 388 - static const struct software_node asus_me176c_bq24190_node = { 389 - .properties = asus_me176c_bq24190_props, 390 - }; 391 - 392 - static const struct property_entry asus_me176c_ug3105_props[] = { 393 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), 394 - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 395 - PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), 396 - { } 397 - }; 398 - 399 - static const struct software_node asus_me176c_ug3105_node = { 400 - .properties = asus_me176c_ug3105_props, 401 - }; 402 - 403 - static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = { 404 - { 405 - /* bq24297 battery charger */ 406 - .board_info = { 407 - .type = "bq24190", 408 - .addr = 0x6b, 409 - .dev_name = "bq24297", 410 - .swnode = &asus_me176c_bq24190_node, 411 - .platform_data = &bq24190_pdata, 412 - }, 413 - .adapter_path = "\\_SB_.I2C1", 414 - .irq_data = { 415 - .type = X86_ACPI_IRQ_TYPE_PMIC, 416 - .chip = "\\_SB_.I2C7.PMIC", 417 - .domain = DOMAIN_BUS_WAKEUP, 418 - .index = 0, 419 - }, 420 - }, { 421 - /* ug3105 battery monitor */ 422 - .board_info = { 423 - .type = "ug3105", 424 - .addr = 0x70, 425 - .dev_name = "ug3105", 426 - .swnode = &asus_me176c_ug3105_node, 427 - }, 428 - .adapter_path = "\\_SB_.I2C1", 429 - }, { 430 - /* ak09911 compass */ 431 - .board_info = { 432 - .type = "ak09911", 433 - .addr = 0x0c, 434 - .dev_name = "ak09911", 435 - }, 436 - .adapter_path = "\\_SB_.I2C5", 437 - }, { 438 - /* kxtj21009 accel */ 439 - .board_info = { 440 - .type = "kxtj21009", 441 - .addr = 0x0f, 442 - .dev_name = "kxtj21009", 443 - .swnode = &asus_me176c_accel_node, 444 - }, 445 - .adapter_path = "\\_SB_.I2C5", 446 - .irq_data = { 447 - .type = X86_ACPI_IRQ_TYPE_APIC, 448 - .index = 0x44, 449 - .trigger = ACPI_EDGE_SENSITIVE, 450 - .polarity = ACPI_ACTIVE_LOW, 451 - }, 452 - }, { 453 - /* goodix touchscreen */ 454 - .board_info = { 455 - .type = "GDIX1001:00", 456 - .addr = 0x14, 457 - .dev_name = "goodix_ts", 458 - }, 459 - .adapter_path = "\\_SB_.I2C6", 460 - .irq_data = { 461 - .type = X86_ACPI_IRQ_TYPE_APIC, 462 - .index = 0x45, 463 - .trigger = ACPI_EDGE_SENSITIVE, 464 - .polarity = ACPI_ACTIVE_LOW, 465 - }, 466 - }, 467 - }; 468 - 469 - static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { 470 - { 471 - .ctrl_hid = "80860F0A", 472 - .ctrl_uid = "2", 473 - .ctrl_devname = "serial0", 474 - .serdev_hid = "BCM2E3A", 475 - }, 476 - }; 477 - 478 - static struct gpiod_lookup_table asus_me176c_goodix_gpios = { 479 - .dev_id = "i2c-goodix_ts", 480 - .table = { 481 - GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH), 482 - GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH), 483 - { } 484 - }, 485 - }; 486 - 487 - static struct gpiod_lookup_table * const asus_me176c_gpios[] = { 488 - &int3496_gpo2_pin22_gpios, 489 - &asus_me176c_goodix_gpios, 490 - NULL 491 - }; 492 - 493 - static const struct x86_dev_info asus_me176c_info __initconst = { 494 - .i2c_client_info = asus_me176c_i2c_clients, 495 - .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), 496 - .pdev_info = asus_me176c_tf103c_pdevs, 497 - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), 498 - .serdev_info = asus_me176c_serdevs, 499 - .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), 500 - .gpiod_lookup_tables = asus_me176c_gpios, 501 - .bat_swnode = &generic_lipo_hv_4v35_battery_node, 502 - .modules = bq24190_modules, 503 - .invalid_aei_gpiochip = "INT33FC:02", 504 - .init = asus_me176c_tf103c_init, 505 - }; 506 - 507 - /* Asus TF103C tablets have an Android factory img with everything hardcoded */ 508 - static const char * const asus_tf103c_accel_mount_matrix[] = { 509 - "0", "-1", "0", 510 - "-1", "0", "0", 511 - "0", "0", "1" 512 - }; 513 - 514 - static const struct property_entry asus_tf103c_accel_props[] = { 515 - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix), 516 - { } 517 - }; 518 - 519 - static const struct software_node asus_tf103c_accel_node = { 520 - .properties = asus_tf103c_accel_props, 521 - }; 522 - 523 - static const struct property_entry asus_tf103c_touchscreen_props[] = { 524 - PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"), 525 - { } 526 - }; 527 - 528 - static const struct software_node asus_tf103c_touchscreen_node = { 529 - .properties = asus_tf103c_touchscreen_props, 530 - }; 531 - 532 - static const struct property_entry asus_tf103c_battery_props[] = { 533 - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), 534 - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), 535 - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), 536 - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), 537 - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), 538 - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), 539 - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 540 - { } 541 - }; 542 - 543 - static const struct software_node asus_tf103c_battery_node = { 544 - .properties = asus_tf103c_battery_props, 545 - }; 546 - 547 - static const struct property_entry asus_tf103c_bq24190_props[] = { 548 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), 549 - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), 550 - PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), 551 - PROPERTY_ENTRY_BOOL("omit-battery-class"), 552 - PROPERTY_ENTRY_BOOL("disable-reset"), 553 - { } 554 - }; 555 - 556 - static const struct software_node asus_tf103c_bq24190_node = { 557 - .properties = asus_tf103c_bq24190_props, 558 - }; 559 - 560 - static const struct property_entry asus_tf103c_ug3105_props[] = { 561 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), 562 - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), 563 - PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), 564 - { } 565 - }; 566 - 567 - static const struct software_node asus_tf103c_ug3105_node = { 568 - .properties = asus_tf103c_ug3105_props, 569 - }; 570 - 571 - static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = { 572 - { 573 - /* bq24297 battery charger */ 574 - .board_info = { 575 - .type = "bq24190", 576 - .addr = 0x6b, 577 - .dev_name = "bq24297", 578 - .swnode = &asus_tf103c_bq24190_node, 579 - .platform_data = &bq24190_pdata, 580 - }, 581 - .adapter_path = "\\_SB_.I2C1", 582 - .irq_data = { 583 - .type = X86_ACPI_IRQ_TYPE_PMIC, 584 - .chip = "\\_SB_.I2C7.PMIC", 585 - .domain = DOMAIN_BUS_WAKEUP, 586 - .index = 0, 587 - }, 588 - }, { 589 - /* ug3105 battery monitor */ 590 - .board_info = { 591 - .type = "ug3105", 592 - .addr = 0x70, 593 - .dev_name = "ug3105", 594 - .swnode = &asus_tf103c_ug3105_node, 595 - }, 596 - .adapter_path = "\\_SB_.I2C1", 597 - }, { 598 - /* ak09911 compass */ 599 - .board_info = { 600 - .type = "ak09911", 601 - .addr = 0x0c, 602 - .dev_name = "ak09911", 603 - }, 604 - .adapter_path = "\\_SB_.I2C5", 605 - }, { 606 - /* kxtj21009 accel */ 607 - .board_info = { 608 - .type = "kxtj21009", 609 - .addr = 0x0f, 610 - .dev_name = "kxtj21009", 611 - .swnode = &asus_tf103c_accel_node, 612 - }, 613 - .adapter_path = "\\_SB_.I2C5", 614 - }, { 615 - /* atmel touchscreen */ 616 - .board_info = { 617 - .type = "atmel_mxt_ts", 618 - .addr = 0x4a, 619 - .dev_name = "atmel_mxt_ts", 620 - .swnode = &asus_tf103c_touchscreen_node, 621 - }, 622 - .adapter_path = "\\_SB_.I2C6", 623 - .irq_data = { 624 - .type = X86_ACPI_IRQ_TYPE_GPIOINT, 625 - .chip = "INT33FC:02", 626 - .index = 28, 627 - .trigger = ACPI_EDGE_SENSITIVE, 628 - .polarity = ACPI_ACTIVE_LOW, 629 - }, 630 - }, 631 - }; 632 - 633 - static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { 634 - &int3496_gpo2_pin22_gpios, 635 - NULL 636 - }; 637 - 638 - static const struct x86_dev_info asus_tf103c_info __initconst = { 639 - .i2c_client_info = asus_tf103c_i2c_clients, 640 - .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), 641 - .pdev_info = asus_me176c_tf103c_pdevs, 642 - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), 643 - .gpiod_lookup_tables = asus_tf103c_gpios, 644 - .bat_swnode = &asus_tf103c_battery_node, 645 - .modules = bq24190_modules, 646 - .invalid_aei_gpiochip = "INT33FC:02", 647 - .init = asus_me176c_tf103c_init, 648 - }; 649 - 650 - /* 651 - * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT 652 - * contains a whole bunch of bogus ACPI I2C devices and is missing entries 653 - * for the touchscreen and the accelerometer. 654 - */ 655 - static const struct property_entry chuwi_hi8_gsl1680_props[] = { 656 - PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), 657 - PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), 658 - PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 659 - PROPERTY_ENTRY_BOOL("silead,home-button"), 660 - PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), 661 - { } 662 - }; 663 - 664 - static const struct software_node chuwi_hi8_gsl1680_node = { 665 - .properties = chuwi_hi8_gsl1680_props, 666 - }; 667 - 668 - static const char * const chuwi_hi8_mount_matrix[] = { 669 - "1", "0", "0", 670 - "0", "-1", "0", 671 - "0", "0", "1" 672 - }; 673 - 674 - static const struct property_entry chuwi_hi8_bma250e_props[] = { 675 - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), 676 - { } 677 - }; 678 - 679 - static const struct software_node chuwi_hi8_bma250e_node = { 680 - .properties = chuwi_hi8_bma250e_props, 681 - }; 682 - 683 - static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { 684 - { 685 - /* Silead touchscreen */ 686 - .board_info = { 687 - .type = "gsl1680", 688 - .addr = 0x40, 689 - .swnode = &chuwi_hi8_gsl1680_node, 690 - }, 691 - .adapter_path = "\\_SB_.I2C4", 692 - .irq_data = { 693 - .type = X86_ACPI_IRQ_TYPE_APIC, 694 - .index = 0x44, 695 - .trigger = ACPI_EDGE_SENSITIVE, 696 - .polarity = ACPI_ACTIVE_HIGH, 697 - }, 698 - }, { 699 - /* BMA250E accelerometer */ 700 - .board_info = { 701 - .type = "bma250e", 702 - .addr = 0x18, 703 - .swnode = &chuwi_hi8_bma250e_node, 704 - }, 705 - .adapter_path = "\\_SB_.I2C3", 706 - .irq_data = { 707 - .type = X86_ACPI_IRQ_TYPE_GPIOINT, 708 - .chip = "INT33FC:02", 709 - .index = 23, 710 - .trigger = ACPI_LEVEL_SENSITIVE, 711 - .polarity = ACPI_ACTIVE_HIGH, 712 - }, 713 - }, 714 - }; 715 - 716 - static int __init chuwi_hi8_init(void) 717 - { 718 - /* 719 - * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() 720 - * breaking the touchscreen + logging various errors when the Windows 721 - * BIOS is used. 722 - */ 723 - if (acpi_dev_present("MSSL0001", NULL, 1)) 724 - return -ENODEV; 725 - 726 - return 0; 727 - } 728 - 729 - static const struct x86_dev_info chuwi_hi8_info __initconst = { 730 - .i2c_client_info = chuwi_hi8_i2c_clients, 731 - .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), 732 - .init = chuwi_hi8_init, 733 - }; 734 - 735 - #define CZC_EC_EXTRA_PORT 0x68 736 - #define CZC_EC_ANDROID_KEYS 0x63 737 - 738 - static int __init czc_p10t_init(void) 739 - { 740 - /* 741 - * The device boots up in "Windows 7" mode, when the home button sends a 742 - * Windows specific key sequence (Left Meta + D) and the second button 743 - * sends an unknown one while also toggling the Radio Kill Switch. 744 - * This is a surprising behavior when the second button is labeled "Back". 745 - * 746 - * The vendor-supplied Android-x86 build switches the device to a "Android" 747 - * mode by writing value 0x63 to the I/O port 0x68. This just seems to just 748 - * set bit 6 on address 0x96 in the EC region; switching the bit directly 749 - * seems to achieve the same result. It uses a "p10t_switcher" to do the 750 - * job. It doesn't seem to be able to do anything else, and no other use 751 - * of the port 0x68 is known. 752 - * 753 - * In the Android mode, the home button sends just a single scancode, 754 - * which can be handled in Linux userspace more reasonably and the back 755 - * button only sends a scancode without toggling the kill switch. 756 - * The scancode can then be mapped either to Back or RF Kill functionality 757 - * in userspace, depending on how the button is labeled on that particular 758 - * model. 759 - */ 760 - outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); 761 - return 0; 762 - } 763 - 764 - static const struct x86_dev_info czc_p10t __initconst = { 765 - .init = czc_p10t_init, 766 - }; 767 - 768 - /* Lenovo Yoga Book X90F / X91F / X91L need manual instantiation of the fg client */ 769 - static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __initconst = { 770 - { 771 - /* BQ27542 fuel-gauge */ 772 - .board_info = { 773 - .type = "bq27542", 774 - .addr = 0x55, 775 - .dev_name = "bq27542", 776 - .swnode = &fg_bq25890_supply_node, 777 - }, 778 - .adapter_path = "\\_SB_.PCI0.I2C1", 779 - }, 780 - }; 781 - 782 - static const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { 783 - .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, 784 - .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), 785 - }; 786 - 787 - /* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ 788 - static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { 789 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), 790 - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 791 - PROPERTY_ENTRY_BOOL("omit-battery-class"), 792 - PROPERTY_ENTRY_BOOL("disable-reset"), 793 - { } 794 - }; 795 - 796 - static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { 797 - .properties = lenovo_yoga_tab2_830_1050_bq24190_props, 798 - }; 799 - 800 - /* This gets filled by lenovo_yoga_tab2_830_1050_init() */ 801 - static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; 802 - 803 - static struct lp855x_platform_data lenovo_yoga_tab2_830_1050_lp8557_pdata = { 804 - .device_control = 0x86, 805 - .initial_brightness = 128, 806 - }; 807 - 808 - static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { 809 - { 810 - /* bq24292i battery charger */ 811 - .board_info = { 812 - .type = "bq24190", 813 - .addr = 0x6b, 814 - .dev_name = "bq24292i", 815 - .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, 816 - .platform_data = &bq24190_pdata, 817 - }, 818 - .adapter_path = "\\_SB_.I2C1", 819 - .irq_data = { 820 - .type = X86_ACPI_IRQ_TYPE_GPIOINT, 821 - .chip = "INT33FC:02", 822 - .index = 2, 823 - .trigger = ACPI_EDGE_SENSITIVE, 824 - .polarity = ACPI_ACTIVE_HIGH, 825 - }, 826 - }, { 827 - /* BQ27541 fuel-gauge */ 828 - .board_info = { 829 - .type = "bq27541", 830 - .addr = 0x55, 831 - .dev_name = "bq27541", 832 - .swnode = &fg_bq24190_supply_node, 833 - }, 834 - .adapter_path = "\\_SB_.I2C1", 835 - }, { 836 - /* Synaptics RMI touchscreen */ 837 - .board_info = { 838 - .type = "rmi4_i2c", 839 - .addr = 0x38, 840 - .dev_name = "rmi4_i2c", 841 - .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, 842 - }, 843 - .adapter_path = "\\_SB_.I2C6", 844 - .irq_data = { 845 - .type = X86_ACPI_IRQ_TYPE_APIC, 846 - .index = 0x45, 847 - .trigger = ACPI_EDGE_SENSITIVE, 848 - .polarity = ACPI_ACTIVE_HIGH, 849 - }, 850 - }, { 851 - /* LP8557 Backlight controller */ 852 - .board_info = { 853 - .type = "lp8557", 854 - .addr = 0x2c, 855 - .dev_name = "lp8557", 856 - .platform_data = &lenovo_yoga_tab2_830_1050_lp8557_pdata, 857 - }, 858 - .adapter_path = "\\_SB_.I2C3", 859 - }, 860 - }; 861 - 862 - static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { 863 - .dev_id = "intel-int3496", 864 - .table = { 865 - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW), 866 - GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH), 867 - { } 868 - }, 869 - }; 870 - 871 - #define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" 872 - 873 - static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { 874 - .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, 875 - .table = { 876 - GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH), 877 - GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH), 878 - GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), 879 - GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW), 880 - { } 881 - }, 882 - }; 883 - 884 - static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { 885 - &lenovo_yoga_tab2_830_1050_int3496_gpios, 886 - &lenovo_yoga_tab2_830_1050_codec_gpios, 887 - NULL 888 - }; 889 - 890 - static int __init lenovo_yoga_tab2_830_1050_init(void); 891 - static void lenovo_yoga_tab2_830_1050_exit(void); 892 - 893 - static struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { 894 - .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, 895 - /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ 896 - .pdev_info = int3496_pdevs, 897 - .pdev_count = ARRAY_SIZE(int3496_pdevs), 898 - .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, 899 - .bat_swnode = &generic_lipo_hv_4v35_battery_node, 900 - .modules = bq24190_modules, 901 - .invalid_aei_gpiochip = "INT33FC:02", 902 - .init = lenovo_yoga_tab2_830_1050_init, 903 - .exit = lenovo_yoga_tab2_830_1050_exit, 904 - }; 905 - 906 - /* 907 - * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same 908 - * mainboard, but they need some different treatment related to the display: 909 - * 1. The 830 uses a portrait LCD panel with a landscape touchscreen, requiring 910 - * the touchscreen driver to adjust the touch-coords to match the LCD. 911 - * 2. Both use an TI LP8557 LED backlight controller. On the 1050 the LP8557's 912 - * PWM input is connected to the PMIC's PWM output and everything works fine 913 - * with the defaults programmed into the LP8557 by the BIOS. 914 - * But on the 830 the LP8557's PWM input is connected to a PWM output coming 915 - * from the LCD panel's controller. The Android code has a hack in the i915 916 - * driver to write the non-standard DSI reg 0x9f with the desired backlight 917 - * level to set the duty-cycle of the LCD's PWM output. 918 - * 919 - * To avoid having to have a similar hack in the mainline kernel the LP8557 920 - * entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the 921 - * LP8557 to directly set the level, ignoring the PWM input. This means that 922 - * the LP8557 i2c_client should only be instantiated on the 830. 923 - */ 924 - static int __init lenovo_yoga_tab2_830_1050_init_display(void) 925 - { 926 - struct gpio_desc *gpiod; 927 - int ret; 928 - 929 - /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ 930 - ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod); 931 - if (ret) 932 - return ret; 933 - 934 - ret = gpiod_get_value_cansleep(gpiod); 935 - if (ret) { 936 - pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); 937 - lenovo_yoga_tab2_830_1050_info.i2c_client_count = 938 - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients) - 1; 939 - } else { 940 - pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); 941 - lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; 942 - lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; 943 - lenovo_yoga_tab2_830_1050_info.i2c_client_count = 944 - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients); 945 - } 946 - 947 - return 0; 948 - } 949 - 950 - /* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ 951 - static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = 952 - PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk", 953 - "INT33FC:02", "pmu_clk2_grp", "pmu_clk"); 954 - 955 - static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; 956 - static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; 957 - 958 - static int __init lenovo_yoga_tab2_830_1050_init_codec(void) 959 - { 960 - struct device *codec_dev; 961 - struct pinctrl *pinctrl; 962 - int ret; 963 - 964 - codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, 965 - LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); 966 - if (!codec_dev) { 967 - pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); 968 - return -ENODEV; 969 - } 970 - 971 - ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1); 972 - if (ret) 973 - goto err_put_device; 974 - 975 - pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk"); 976 - if (IS_ERR(pinctrl)) { 977 - ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n"); 978 - goto err_unregister_mappings; 979 - } 980 - 981 - /* We're done with the codec_dev now */ 982 - put_device(codec_dev); 983 - 984 - lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; 985 - return 0; 986 - 987 - err_unregister_mappings: 988 - pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); 989 - err_put_device: 990 - put_device(codec_dev); 991 - return ret; 992 - } 993 - 994 - /* 995 - * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off 996 - * gets used as pm_power_off handler. This causes "poweroff" on these tablets 997 - * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* 998 - * followed by a normal 3 second press to recover. Avoid this by doing an EFI 999 - * poweroff instead. 1000 - */ 1001 - static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) 1002 - { 1003 - efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); 1004 - 1005 - return NOTIFY_DONE; 1006 - } 1007 - 1008 - static int __init lenovo_yoga_tab2_830_1050_init(void) 1009 - { 1010 - int ret; 1011 - 1012 - ret = lenovo_yoga_tab2_830_1050_init_display(); 1013 - if (ret) 1014 - return ret; 1015 - 1016 - ret = lenovo_yoga_tab2_830_1050_init_codec(); 1017 - if (ret) 1018 - return ret; 1019 - 1020 - /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ 1021 - lenovo_yoga_tab2_830_1050_sys_off_handler = 1022 - register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, 1023 - lenovo_yoga_tab2_830_1050_power_off, NULL); 1024 - if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler)) 1025 - return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler); 1026 - 1027 - return 0; 1028 - } 1029 - 1030 - static void lenovo_yoga_tab2_830_1050_exit(void) 1031 - { 1032 - unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler); 1033 - 1034 - if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { 1035 - pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl); 1036 - pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); 1037 - } 1038 - } 1039 - 1040 - /* Lenovo Yoga Tab 3 Pro YT3-X90F */ 1041 - 1042 - /* 1043 - * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, 1044 - * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. 1045 - */ 1046 - static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; 1047 - static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; 1048 - 1049 - static const struct property_entry fg_bq25890_1_supply_props[] = { 1050 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), 1051 - { } 1052 - }; 1053 - 1054 - static const struct software_node fg_bq25890_1_supply_node = { 1055 - .properties = fg_bq25890_1_supply_props, 1056 - }; 1057 - 1058 - /* bq25892 charger settings for the flat lipo battery behind the screen */ 1059 - static const struct property_entry lenovo_yt3_bq25892_0_props[] = { 1060 - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), 1061 - PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), 1062 - PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), 1063 - PROPERTY_ENTRY_BOOL("linux,skip-reset"), 1064 - /* Values taken from Android Factory Image */ 1065 - PROPERTY_ENTRY_U32("ti,charge-current", 2048000), 1066 - PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), 1067 - PROPERTY_ENTRY_U32("ti,termination-current", 128000), 1068 - PROPERTY_ENTRY_U32("ti,precharge-current", 128000), 1069 - PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), 1070 - PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), 1071 - PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), 1072 - PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), 1073 - { } 1074 - }; 1075 - 1076 - static const struct software_node lenovo_yt3_bq25892_0_node = { 1077 - .properties = lenovo_yt3_bq25892_0_props, 1078 - }; 1079 - 1080 - static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { 1081 - { 1082 - /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ 1083 - .board_info = { 1084 - .type = "bq27500", 1085 - .addr = 0x55, 1086 - .dev_name = "bq27500_0", 1087 - .swnode = &fg_bq25890_supply_node, 1088 - }, 1089 - .adapter_path = "\\_SB_.PCI0.I2C1", 1090 - }, { 1091 - /* bq25892 charger for the flat lipo battery behind the screen */ 1092 - .board_info = { 1093 - .type = "bq25892", 1094 - .addr = 0x6b, 1095 - .dev_name = "bq25892_0", 1096 - .swnode = &lenovo_yt3_bq25892_0_node, 1097 - }, 1098 - .adapter_path = "\\_SB_.PCI0.I2C1", 1099 - .irq_data = { 1100 - .type = X86_ACPI_IRQ_TYPE_GPIOINT, 1101 - .chip = "INT33FF:01", 1102 - .index = 5, 1103 - .trigger = ACPI_EDGE_SENSITIVE, 1104 - .polarity = ACPI_ACTIVE_LOW, 1105 - }, 1106 - }, { 1107 - /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ 1108 - .board_info = { 1109 - .type = "bq27500", 1110 - .addr = 0x55, 1111 - .dev_name = "bq27500_1", 1112 - .swnode = &fg_bq25890_1_supply_node, 1113 - }, 1114 - .adapter_path = "\\_SB_.PCI0.I2C2", 1115 - } 1116 - }; 1117 - 1118 - static int __init lenovo_yt3_init(void) 1119 - { 1120 - struct gpio_desc *gpiod; 1121 - int ret; 1122 - 1123 - /* 1124 - * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins 1125 - * connected to GPIOs, rather then having them hardwired to the correct 1126 - * values as is normally done. 1127 - * 1128 - * The bq25890_charger driver controls these through I2C, but this only 1129 - * works if not overridden by the pins. Set these pins here: 1130 - * 1. Set /CE to 0 to allow charging. 1131 - * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of 1132 - * the main "bq25892_1" charger is used when necessary. 1133 - */ 1134 - 1135 - /* /CE pin */ 1136 - ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); 1137 - if (ret < 0) 1138 - return ret; 1139 - 1140 - /* 1141 - * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" 1142 - * gpio_desc, that is there is no way to pass lookup-flags like 1143 - * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since 1144 - * the /CE pin is active-low, but not marked as such in the gpio_desc. 1145 - */ 1146 - gpiod_set_value(gpiod, 0); 1147 - 1148 - /* OTG pin */ 1149 - ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); 1150 - if (ret < 0) 1151 - return ret; 1152 - 1153 - gpiod_set_value(gpiod, 0); 1154 - 1155 - return 0; 1156 - } 1157 - 1158 - static const struct x86_dev_info lenovo_yt3_info __initconst = { 1159 - .i2c_client_info = lenovo_yt3_i2c_clients, 1160 - .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), 1161 - .init = lenovo_yt3_init, 1162 - }; 1163 - 1164 - /* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ 1165 - static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { 1166 - "0", "1", "0", 1167 - "1", "0", "0", 1168 - "0", "0", "1" 1169 - }; 1170 - 1171 - static const struct property_entry medion_lifetab_s10346_accel_props[] = { 1172 - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), 1173 - { } 1174 - }; 1175 - 1176 - static const struct software_node medion_lifetab_s10346_accel_node = { 1177 - .properties = medion_lifetab_s10346_accel_props, 1178 - }; 1179 - 1180 - /* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ 1181 - static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { 1182 - PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), 1183 - PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 1184 - { } 1185 - }; 1186 - 1187 - static const struct software_node medion_lifetab_s10346_touchscreen_node = { 1188 - .properties = medion_lifetab_s10346_touchscreen_props, 1189 - }; 1190 - 1191 - static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { 1192 - { 1193 - /* kxtj21009 accel */ 1194 - .board_info = { 1195 - .type = "kxtj21009", 1196 - .addr = 0x0f, 1197 - .dev_name = "kxtj21009", 1198 - .swnode = &medion_lifetab_s10346_accel_node, 1199 - }, 1200 - .adapter_path = "\\_SB_.I2C3", 1201 - .irq_data = { 1202 - .type = X86_ACPI_IRQ_TYPE_GPIOINT, 1203 - .chip = "INT33FC:02", 1204 - .index = 23, 1205 - .trigger = ACPI_EDGE_SENSITIVE, 1206 - .polarity = ACPI_ACTIVE_HIGH, 1207 - }, 1208 - }, { 1209 - /* goodix touchscreen */ 1210 - .board_info = { 1211 - .type = "GDIX1001:00", 1212 - .addr = 0x14, 1213 - .dev_name = "goodix_ts", 1214 - .swnode = &medion_lifetab_s10346_touchscreen_node, 1215 - }, 1216 - .adapter_path = "\\_SB_.I2C4", 1217 - .irq_data = { 1218 - .type = X86_ACPI_IRQ_TYPE_APIC, 1219 - .index = 0x44, 1220 - .trigger = ACPI_EDGE_SENSITIVE, 1221 - .polarity = ACPI_ACTIVE_LOW, 1222 - }, 1223 - }, 1224 - }; 1225 - 1226 - static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { 1227 - .dev_id = "i2c-goodix_ts", 1228 - .table = { 1229 - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), 1230 - GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), 1231 - { } 1232 - }, 1233 - }; 1234 - 1235 - static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { 1236 - &medion_lifetab_s10346_goodix_gpios, 1237 - NULL 1238 - }; 1239 - 1240 - static const struct x86_dev_info medion_lifetab_s10346_info __initconst = { 1241 - .i2c_client_info = medion_lifetab_s10346_i2c_clients, 1242 - .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), 1243 - .gpiod_lookup_tables = medion_lifetab_s10346_gpios, 1244 - }; 1245 - 1246 - /* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ 1247 - static const char * const nextbook_ares8_accel_mount_matrix[] = { 1248 - "0", "-1", "0", 1249 - "-1", "0", "0", 1250 - "0", "0", "1" 1251 - }; 1252 - 1253 - static const struct property_entry nextbook_ares8_accel_props[] = { 1254 - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), 1255 - { } 1256 - }; 1257 - 1258 - static const struct software_node nextbook_ares8_accel_node = { 1259 - .properties = nextbook_ares8_accel_props, 1260 - }; 1261 - 1262 - static const struct property_entry nextbook_ares8_touchscreen_props[] = { 1263 - PROPERTY_ENTRY_U32("touchscreen-size-x", 800), 1264 - PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), 1265 - { } 1266 - }; 1267 - 1268 - static const struct software_node nextbook_ares8_touchscreen_node = { 1269 - .properties = nextbook_ares8_touchscreen_props, 1270 - }; 1271 - 1272 - static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { 1273 - { 1274 - /* Freescale MMA8653FC accel */ 1275 - .board_info = { 1276 - .type = "mma8653", 1277 - .addr = 0x1d, 1278 - .dev_name = "mma8653", 1279 - .swnode = &nextbook_ares8_accel_node, 1280 - }, 1281 - .adapter_path = "\\_SB_.I2C3", 1282 - }, { 1283 - /* FT5416DQ9 touchscreen controller */ 1284 - .board_info = { 1285 - .type = "edt-ft5x06", 1286 - .addr = 0x38, 1287 - .dev_name = "ft5416", 1288 - .swnode = &nextbook_ares8_touchscreen_node, 1289 - }, 1290 - .adapter_path = "\\_SB_.I2C4", 1291 - .irq_data = { 1292 - .type = X86_ACPI_IRQ_TYPE_GPIOINT, 1293 - .chip = "INT33FC:02", 1294 - .index = 3, 1295 - .trigger = ACPI_EDGE_SENSITIVE, 1296 - .polarity = ACPI_ACTIVE_LOW, 1297 - }, 1298 - }, 1299 - }; 1300 - 1301 - static struct gpiod_lookup_table nextbook_ares8_int3496_gpios = { 1302 - .dev_id = "intel-int3496", 1303 - .table = { 1304 - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), 1305 - GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), 1306 - { } 1307 - }, 1308 - }; 1309 - 1310 - static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { 1311 - &nextbook_ares8_int3496_gpios, 1312 - NULL 1313 - }; 1314 - 1315 - static const struct x86_dev_info nextbook_ares8_info __initconst = { 1316 - .i2c_client_info = nextbook_ares8_i2c_clients, 1317 - .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), 1318 - .pdev_info = int3496_pdevs, 1319 - .pdev_count = ARRAY_SIZE(int3496_pdevs), 1320 - .gpiod_lookup_tables = nextbook_ares8_gpios, 1321 - .invalid_aei_gpiochip = "INT33FC:02", 1322 - }; 1323 - 1324 - /* 1325 - * Whitelabel (sold as various brands) TM800A550L tablets. 1326 - * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices 1327 - * (removed through acpi_quirk_skip_i2c_client_enumeration()) and 1328 - * the touchscreen fwnode has the wrong GPIOs. 1329 - */ 1330 - static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { 1331 - "-1", "0", "0", 1332 - "0", "1", "0", 1333 - "0", "0", "1" 1334 - }; 1335 - 1336 - static const struct property_entry whitelabel_tm800a550l_accel_props[] = { 1337 - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), 1338 - { } 1339 - }; 1340 - 1341 - static const struct software_node whitelabel_tm800a550l_accel_node = { 1342 - .properties = whitelabel_tm800a550l_accel_props, 1343 - }; 1344 - 1345 - static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { 1346 - PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), 1347 - PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), 1348 - PROPERTY_ENTRY_U32("goodix,main-clk", 54), 1349 - { } 1350 - }; 1351 - 1352 - static const struct software_node whitelabel_tm800a550l_goodix_node = { 1353 - .properties = whitelabel_tm800a550l_goodix_props, 1354 - }; 1355 - 1356 - static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { 1357 - { 1358 - /* goodix touchscreen */ 1359 - .board_info = { 1360 - .type = "GDIX1001:00", 1361 - .addr = 0x14, 1362 - .dev_name = "goodix_ts", 1363 - .swnode = &whitelabel_tm800a550l_goodix_node, 1364 - }, 1365 - .adapter_path = "\\_SB_.I2C2", 1366 - .irq_data = { 1367 - .type = X86_ACPI_IRQ_TYPE_APIC, 1368 - .index = 0x44, 1369 - .trigger = ACPI_EDGE_SENSITIVE, 1370 - .polarity = ACPI_ACTIVE_HIGH, 1371 - }, 1372 - }, { 1373 - /* kxcj91008 accel */ 1374 - .board_info = { 1375 - .type = "kxcj91008", 1376 - .addr = 0x0f, 1377 - .dev_name = "kxcj91008", 1378 - .swnode = &whitelabel_tm800a550l_accel_node, 1379 - }, 1380 - .adapter_path = "\\_SB_.I2C3", 1381 - }, 1382 - }; 1383 - 1384 - static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { 1385 - .dev_id = "i2c-goodix_ts", 1386 - .table = { 1387 - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), 1388 - GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), 1389 - { } 1390 - }, 1391 - }; 1392 - 1393 - static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { 1394 - &whitelabel_tm800a550l_goodix_gpios, 1395 - NULL 1396 - }; 1397 - 1398 - static const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { 1399 - .i2c_client_info = whitelabel_tm800a550l_i2c_clients, 1400 - .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), 1401 - .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, 1402 - }; 1403 - 1404 - /* 1405 - * If the EFI bootloader is not Xiaomi's own signed Android loader, then the 1406 - * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing 1407 - * a bunch of devices to be hidden. 1408 - * 1409 - * This takes care of instantiating the hidden devices manually. 1410 - */ 1411 - static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { 1412 - { 1413 - /* BQ27520 fuel-gauge */ 1414 - .board_info = { 1415 - .type = "bq27520", 1416 - .addr = 0x55, 1417 - .dev_name = "bq27520", 1418 - .swnode = &fg_bq25890_supply_node, 1419 - }, 1420 - .adapter_path = "\\_SB_.PCI0.I2C1", 1421 - }, { 1422 - /* KTD2026 RGB notification LED controller */ 1423 - .board_info = { 1424 - .type = "ktd2026", 1425 - .addr = 0x30, 1426 - .dev_name = "ktd2026", 1427 - }, 1428 - .adapter_path = "\\_SB_.PCI0.I2C3", 1429 - }, 1430 - }; 1431 - 1432 - static const struct x86_dev_info xiaomi_mipad2_info __initconst = { 1433 - .i2c_client_info = xiaomi_mipad2_i2c_clients, 1434 - .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), 1435 - }; 1436 - 1437 - static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { 1438 - { 1439 - /* Advantech MICA-071 */ 1440 - .matches = { 1441 - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), 1442 - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), 1443 - }, 1444 - .driver_data = (void *)&advantech_mica_071_info, 1445 - }, 1446 - { 1447 - /* Asus MeMO Pad 7 ME176C */ 1448 - .matches = { 1449 - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 1450 - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), 1451 - }, 1452 - .driver_data = (void *)&asus_me176c_info, 1453 - }, 1454 - { 1455 - /* Asus TF103C */ 1456 - .matches = { 1457 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 1458 - DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), 1459 - }, 1460 - .driver_data = (void *)&asus_tf103c_info, 1461 - }, 1462 - { 1463 - /* Chuwi Hi8 (CWI509) */ 1464 - .matches = { 1465 - DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), 1466 - DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"), 1467 - DMI_MATCH(DMI_SYS_VENDOR, "ilife"), 1468 - DMI_MATCH(DMI_PRODUCT_NAME, "S806"), 1469 - }, 1470 - .driver_data = (void *)&chuwi_hi8_info, 1471 - }, 1472 - { 1473 - /* CZC P10T */ 1474 - .ident = "CZC ODEON TPC-10 (\"P10T\")", 1475 - .matches = { 1476 - DMI_MATCH(DMI_SYS_VENDOR, "CZC"), 1477 - DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"), 1478 - }, 1479 - .driver_data = (void *)&czc_p10t, 1480 - }, 1481 - { 1482 - /* CZC P10T variant */ 1483 - .ident = "ViewSonic ViewPad 10", 1484 - .matches = { 1485 - DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"), 1486 - DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"), 1487 - }, 1488 - .driver_data = (void *)&czc_p10t, 1489 - }, 1490 - { 1491 - /* Lenovo Yoga Book X90F / X91F / X91L */ 1492 - .matches = { 1493 - /* Non exact match to match all versions */ 1494 - DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), 1495 - }, 1496 - .driver_data = (void *)&lenovo_yogabook_x9x_info, 1497 - }, 1498 - { 1499 - /* 1500 - * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" 1501 - * Lenovo Yoga Tablet 2 use the same mainboard) 1502 - */ 1503 - .matches = { 1504 - DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), 1505 - DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), 1506 - DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), 1507 - /* Partial match on beginning of BIOS version */ 1508 - DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), 1509 - }, 1510 - .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, 1511 - }, 1512 - { 1513 - /* Lenovo Yoga Tab 3 Pro YT3-X90F */ 1514 - .matches = { 1515 - DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 1516 - DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 1517 - DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), 1518 - }, 1519 - .driver_data = (void *)&lenovo_yt3_info, 1520 - }, 1521 - { 1522 - /* Medion Lifetab S10346 */ 1523 - .matches = { 1524 - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 1525 - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), 1526 - /* Above strings are much too generic, also match on BIOS date */ 1527 - DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), 1528 - }, 1529 - .driver_data = (void *)&medion_lifetab_s10346_info, 1530 - }, 1531 - { 1532 - /* Nextbook Ares 8 */ 1533 - .matches = { 1534 - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), 1535 - DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), 1536 - }, 1537 - .driver_data = (void *)&nextbook_ares8_info, 1538 - }, 1539 - { 1540 - /* Whitelabel (sold as various brands) TM800A550L */ 1541 - .matches = { 1542 - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 1543 - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), 1544 - /* Above strings are too generic, also match on BIOS version */ 1545 - DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), 1546 - }, 1547 - .driver_data = (void *)&whitelabel_tm800a550l_info, 1548 - }, 1549 - { 1550 - /* Xiaomi Mi Pad 2 */ 1551 - .matches = { 1552 - DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), 1553 - DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), 1554 - }, 1555 - .driver_data = (void *)&xiaomi_mipad2_info, 1556 - }, 1557 - { } 1558 - }; 1559 - MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids); 1560 - 1561 - static int i2c_client_count; 1562 - static int pdev_count; 1563 - static int serdev_count; 1564 - static struct i2c_client **i2c_clients; 1565 - static struct platform_device **pdevs; 1566 - static struct serdev_device **serdevs; 1567 - static struct gpiod_lookup_table * const *gpiod_lookup_tables; 1568 - static const struct software_node *bat_swnode; 1569 - static void (*exit_handler)(void); 1570 - 1571 - static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, 1572 - int idx) 1573 - { 1574 - const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; 1575 - struct i2c_board_info board_info = client_info->board_info; 1576 - struct i2c_adapter *adap; 1577 - acpi_handle handle; 1578 - acpi_status status; 1579 - 1580 - board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); 1581 - if (board_info.irq < 0) 1582 - return board_info.irq; 1583 - 1584 - status = acpi_get_handle(NULL, client_info->adapter_path, &handle); 1585 - if (ACPI_FAILURE(status)) { 1586 - pr_err("Error could not get %s handle\n", client_info->adapter_path); 1587 - return -ENODEV; 1588 - } 1589 - 1590 - adap = i2c_acpi_find_adapter_by_handle(handle); 1591 - if (!adap) { 1592 - pr_err("error could not get %s adapter\n", client_info->adapter_path); 1593 - return -ENODEV; 1594 - } 1595 - 1596 - i2c_clients[idx] = i2c_new_client_device(adap, &board_info); 1597 - put_device(&adap->dev); 1598 - if (IS_ERR(i2c_clients[idx])) 1599 - return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), 1600 - "creating I2C-client %d\n", idx); 1601 - 1602 - return 0; 1603 - } 1604 - 1605 - static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) 1606 - { 1607 - struct acpi_device *ctrl_adev, *serdev_adev; 1608 - struct serdev_device *serdev; 1609 - struct device *ctrl_dev; 1610 - int ret = -ENODEV; 1611 - 1612 - ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); 1613 - if (!ctrl_adev) { 1614 - pr_err("error could not get %s/%s ctrl adev\n", 1615 - info->ctrl_hid, info->ctrl_uid); 1616 - return -ENODEV; 1617 - } 1618 - 1619 - serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); 1620 - if (!serdev_adev) { 1621 - pr_err("error could not get %s serdev adev\n", info->serdev_hid); 1622 - goto put_ctrl_adev; 1623 - } 1624 - 1625 - /* get_first_physical_node() returns a weak ref, no need to put() it */ 1626 - ctrl_dev = acpi_get_first_physical_node(ctrl_adev); 1627 - if (!ctrl_dev) { 1628 - pr_err("error could not get %s/%s ctrl physical dev\n", 1629 - info->ctrl_hid, info->ctrl_uid); 1630 - goto put_serdev_adev; 1631 - } 1632 - 1633 - /* ctrl_dev now points to the controller's parent, get the controller */ 1634 - ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); 1635 - if (!ctrl_dev) { 1636 - pr_err("error could not get %s/%s %s ctrl dev\n", 1637 - info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); 1638 - goto put_serdev_adev; 1639 - } 1640 - 1641 - serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); 1642 - if (!serdev) { 1643 - ret = -ENOMEM; 1644 - goto put_serdev_adev; 1645 - } 1646 - 1647 - ACPI_COMPANION_SET(&serdev->dev, serdev_adev); 1648 - acpi_device_set_enumerated(serdev_adev); 1649 - 1650 - ret = serdev_device_add(serdev); 1651 - if (ret) { 1652 - dev_err(&serdev->dev, "error %d adding serdev\n", ret); 1653 - serdev_device_put(serdev); 1654 - goto put_serdev_adev; 1655 - } 1656 - 1657 - serdevs[idx] = serdev; 1658 - 1659 - put_serdev_adev: 1660 - acpi_dev_put(serdev_adev); 1661 - put_ctrl_adev: 1662 - acpi_dev_put(ctrl_adev); 1663 - return ret; 1664 - } 1665 - 1666 - static void x86_android_tablet_cleanup(void) 1667 - { 1668 - int i; 1669 - 1670 - for (i = 0; i < serdev_count; i++) { 1671 - if (serdevs[i]) 1672 - serdev_device_remove(serdevs[i]); 1673 - } 1674 - 1675 - kfree(serdevs); 1676 - 1677 - for (i = 0; i < pdev_count; i++) 1678 - platform_device_unregister(pdevs[i]); 1679 - 1680 - kfree(pdevs); 1681 - 1682 - for (i = 0; i < i2c_client_count; i++) 1683 - i2c_unregister_device(i2c_clients[i]); 1684 - 1685 - kfree(i2c_clients); 1686 - 1687 - if (exit_handler) 1688 - exit_handler(); 1689 - 1690 - for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) 1691 - gpiod_remove_lookup_table(gpiod_lookup_tables[i]); 1692 - 1693 - software_node_unregister(bat_swnode); 1694 - } 1695 - 1696 - static __init int x86_android_tablet_init(void) 1697 - { 1698 - const struct x86_dev_info *dev_info; 1699 - const struct dmi_system_id *id; 1700 - struct gpio_chip *chip; 1701 - int i, ret = 0; 1702 - 1703 - id = dmi_first_match(x86_android_tablet_ids); 1704 - if (!id) 1705 - return -ENODEV; 1706 - 1707 - dev_info = id->driver_data; 1708 - 1709 - /* 1710 - * The broken DSDTs on these devices often also include broken 1711 - * _AEI (ACPI Event Interrupt) handlers, disable these. 1712 - */ 1713 - if (dev_info->invalid_aei_gpiochip) { 1714 - chip = gpiochip_find(dev_info->invalid_aei_gpiochip, 1715 - gpiochip_find_match_label); 1716 - if (!chip) { 1717 - pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip); 1718 - return -ENODEV; 1719 - } 1720 - acpi_gpiochip_free_interrupts(chip); 1721 - } 1722 - 1723 - /* 1724 - * Since this runs from module_init() it cannot use -EPROBE_DEFER, 1725 - * instead pre-load any modules which are listed as requirements. 1726 - */ 1727 - for (i = 0; dev_info->modules && dev_info->modules[i]; i++) 1728 - request_module(dev_info->modules[i]); 1729 - 1730 - bat_swnode = dev_info->bat_swnode; 1731 - if (bat_swnode) { 1732 - ret = software_node_register(bat_swnode); 1733 - if (ret) 1734 - return ret; 1735 - } 1736 - 1737 - gpiod_lookup_tables = dev_info->gpiod_lookup_tables; 1738 - for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) 1739 - gpiod_add_lookup_table(gpiod_lookup_tables[i]); 1740 - 1741 - if (dev_info->init) { 1742 - ret = dev_info->init(); 1743 - if (ret < 0) { 1744 - x86_android_tablet_cleanup(); 1745 - return ret; 1746 - } 1747 - exit_handler = dev_info->exit; 1748 - } 1749 - 1750 - i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); 1751 - if (!i2c_clients) { 1752 - x86_android_tablet_cleanup(); 1753 - return -ENOMEM; 1754 - } 1755 - 1756 - i2c_client_count = dev_info->i2c_client_count; 1757 - for (i = 0; i < i2c_client_count; i++) { 1758 - ret = x86_instantiate_i2c_client(dev_info, i); 1759 - if (ret < 0) { 1760 - x86_android_tablet_cleanup(); 1761 - return ret; 1762 - } 1763 - } 1764 - 1765 - pdevs = kcalloc(dev_info->pdev_count, sizeof(*pdevs), GFP_KERNEL); 1766 - if (!pdevs) { 1767 - x86_android_tablet_cleanup(); 1768 - return -ENOMEM; 1769 - } 1770 - 1771 - pdev_count = dev_info->pdev_count; 1772 - for (i = 0; i < pdev_count; i++) { 1773 - pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); 1774 - if (IS_ERR(pdevs[i])) { 1775 - x86_android_tablet_cleanup(); 1776 - return PTR_ERR(pdevs[i]); 1777 - } 1778 - } 1779 - 1780 - serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); 1781 - if (!serdevs) { 1782 - x86_android_tablet_cleanup(); 1783 - return -ENOMEM; 1784 - } 1785 - 1786 - serdev_count = dev_info->serdev_count; 1787 - for (i = 0; i < serdev_count; i++) { 1788 - ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); 1789 - if (ret < 0) { 1790 - x86_android_tablet_cleanup(); 1791 - return ret; 1792 - } 1793 - } 1794 - 1795 - return 0; 1796 - } 1797 - 1798 - module_init(x86_android_tablet_init); 1799 - module_exit(x86_android_tablet_cleanup); 1800 - 1801 - MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 1802 - MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); 1803 - MODULE_LICENSE("GPL");
+21
drivers/platform/x86/x86-android-tablets/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + # 3 + # X86 Android tablet support Kconfig 4 + # 5 + 6 + config X86_ANDROID_TABLETS 7 + tristate "X86 Android tablet support" 8 + depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB && PMIC_OPREGION 9 + help 10 + X86 tablets which ship with Android as (part of) the factory image 11 + typically have various problems with their DSDTs. The factory kernels 12 + shipped on these devices typically have device addresses and GPIOs 13 + hardcoded in the kernel, rather than specified in their DSDT. 14 + 15 + With the DSDT containing a random collection of devices which may or 16 + may not actually be present. This driver contains various fixes for 17 + such tablets, including instantiating kernel devices for devices which 18 + are missing from the DSDT. 19 + 20 + If you have a x86 Android tablet say Y or M here, for a generic x86 21 + distro config say M here.
+9
drivers/platform/x86/x86-android-tablets/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + # 3 + # X86 Android tablet support Makefile 4 + # 5 + 6 + obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o 7 + 8 + x86-android-tablets-y := core.o dmi.o shared-psy-info.o \ 9 + asus.o lenovo.o other.o
+325
drivers/platform/x86/x86-android-tablets/asus.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Board info for Asus X86 tablets which ship with Android as the factory image 4 + * and which have broken DSDT tables. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #include <linux/gpio/machine.h> 12 + #include <linux/input.h> 13 + #include <linux/platform_device.h> 14 + 15 + #include "shared-psy-info.h" 16 + #include "x86-android-tablets.h" 17 + 18 + /* Asus ME176C and TF103C tablets shared data */ 19 + static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { 20 + .dev_id = "intel-int3496", 21 + .table = { 22 + GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH), 23 + { } 24 + }, 25 + }; 26 + 27 + static struct x86_gpio_button asus_me176c_tf103c_lid = { 28 + .button = { 29 + .code = SW_LID, 30 + .active_low = true, 31 + .desc = "lid_sw", 32 + .type = EV_SW, 33 + .wakeup = true, 34 + .debounce_interval = 50, 35 + }, 36 + .chip = "INT33FC:02", 37 + .pin = 12, 38 + }; 39 + 40 + /* Asus ME176C tablets have an Android factory img with everything hardcoded */ 41 + static const char * const asus_me176c_accel_mount_matrix[] = { 42 + "-1", "0", "0", 43 + "0", "1", "0", 44 + "0", "0", "1" 45 + }; 46 + 47 + static const struct property_entry asus_me176c_accel_props[] = { 48 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix), 49 + { } 50 + }; 51 + 52 + static const struct software_node asus_me176c_accel_node = { 53 + .properties = asus_me176c_accel_props, 54 + }; 55 + 56 + static const struct property_entry asus_me176c_bq24190_props[] = { 57 + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), 58 + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 59 + PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), 60 + PROPERTY_ENTRY_BOOL("omit-battery-class"), 61 + PROPERTY_ENTRY_BOOL("disable-reset"), 62 + { } 63 + }; 64 + 65 + static const struct software_node asus_me176c_bq24190_node = { 66 + .properties = asus_me176c_bq24190_props, 67 + }; 68 + 69 + static const struct property_entry asus_me176c_ug3105_props[] = { 70 + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), 71 + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 72 + PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), 73 + { } 74 + }; 75 + 76 + static const struct software_node asus_me176c_ug3105_node = { 77 + .properties = asus_me176c_ug3105_props, 78 + }; 79 + 80 + static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = { 81 + { 82 + /* bq24297 battery charger */ 83 + .board_info = { 84 + .type = "bq24190", 85 + .addr = 0x6b, 86 + .dev_name = "bq24297", 87 + .swnode = &asus_me176c_bq24190_node, 88 + .platform_data = &bq24190_pdata, 89 + }, 90 + .adapter_path = "\\_SB_.I2C1", 91 + .irq_data = { 92 + .type = X86_ACPI_IRQ_TYPE_PMIC, 93 + .chip = "\\_SB_.I2C7.PMIC", 94 + .domain = DOMAIN_BUS_WAKEUP, 95 + .index = 0, 96 + }, 97 + }, { 98 + /* ug3105 battery monitor */ 99 + .board_info = { 100 + .type = "ug3105", 101 + .addr = 0x70, 102 + .dev_name = "ug3105", 103 + .swnode = &asus_me176c_ug3105_node, 104 + }, 105 + .adapter_path = "\\_SB_.I2C1", 106 + }, { 107 + /* ak09911 compass */ 108 + .board_info = { 109 + .type = "ak09911", 110 + .addr = 0x0c, 111 + .dev_name = "ak09911", 112 + }, 113 + .adapter_path = "\\_SB_.I2C5", 114 + }, { 115 + /* kxtj21009 accel */ 116 + .board_info = { 117 + .type = "kxtj21009", 118 + .addr = 0x0f, 119 + .dev_name = "kxtj21009", 120 + .swnode = &asus_me176c_accel_node, 121 + }, 122 + .adapter_path = "\\_SB_.I2C5", 123 + .irq_data = { 124 + .type = X86_ACPI_IRQ_TYPE_APIC, 125 + .index = 0x44, 126 + .trigger = ACPI_EDGE_SENSITIVE, 127 + .polarity = ACPI_ACTIVE_LOW, 128 + }, 129 + }, { 130 + /* goodix touchscreen */ 131 + .board_info = { 132 + .type = "GDIX1001:00", 133 + .addr = 0x14, 134 + .dev_name = "goodix_ts", 135 + }, 136 + .adapter_path = "\\_SB_.I2C6", 137 + .irq_data = { 138 + .type = X86_ACPI_IRQ_TYPE_APIC, 139 + .index = 0x45, 140 + .trigger = ACPI_EDGE_SENSITIVE, 141 + .polarity = ACPI_ACTIVE_LOW, 142 + }, 143 + }, 144 + }; 145 + 146 + static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { 147 + { 148 + .ctrl_hid = "80860F0A", 149 + .ctrl_uid = "2", 150 + .ctrl_devname = "serial0", 151 + .serdev_hid = "BCM2E3A", 152 + }, 153 + }; 154 + 155 + static struct gpiod_lookup_table asus_me176c_goodix_gpios = { 156 + .dev_id = "i2c-goodix_ts", 157 + .table = { 158 + GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH), 159 + GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH), 160 + { } 161 + }, 162 + }; 163 + 164 + static struct gpiod_lookup_table * const asus_me176c_gpios[] = { 165 + &int3496_gpo2_pin22_gpios, 166 + &asus_me176c_goodix_gpios, 167 + NULL 168 + }; 169 + 170 + const struct x86_dev_info asus_me176c_info __initconst = { 171 + .i2c_client_info = asus_me176c_i2c_clients, 172 + .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), 173 + .pdev_info = int3496_pdevs, 174 + .pdev_count = 1, 175 + .serdev_info = asus_me176c_serdevs, 176 + .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), 177 + .gpio_button = &asus_me176c_tf103c_lid, 178 + .gpiod_lookup_tables = asus_me176c_gpios, 179 + .bat_swnode = &generic_lipo_hv_4v35_battery_node, 180 + .modules = bq24190_modules, 181 + .invalid_aei_gpiochip = "INT33FC:02", 182 + }; 183 + 184 + /* Asus TF103C tablets have an Android factory img with everything hardcoded */ 185 + static const char * const asus_tf103c_accel_mount_matrix[] = { 186 + "0", "-1", "0", 187 + "-1", "0", "0", 188 + "0", "0", "1" 189 + }; 190 + 191 + static const struct property_entry asus_tf103c_accel_props[] = { 192 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix), 193 + { } 194 + }; 195 + 196 + static const struct software_node asus_tf103c_accel_node = { 197 + .properties = asus_tf103c_accel_props, 198 + }; 199 + 200 + static const struct property_entry asus_tf103c_touchscreen_props[] = { 201 + PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"), 202 + { } 203 + }; 204 + 205 + static const struct software_node asus_tf103c_touchscreen_node = { 206 + .properties = asus_tf103c_touchscreen_props, 207 + }; 208 + 209 + static const struct property_entry asus_tf103c_battery_props[] = { 210 + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), 211 + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), 212 + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), 213 + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), 214 + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), 215 + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), 216 + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 217 + { } 218 + }; 219 + 220 + static const struct software_node asus_tf103c_battery_node = { 221 + .properties = asus_tf103c_battery_props, 222 + }; 223 + 224 + static const struct property_entry asus_tf103c_bq24190_props[] = { 225 + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), 226 + PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), 227 + PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), 228 + PROPERTY_ENTRY_BOOL("omit-battery-class"), 229 + PROPERTY_ENTRY_BOOL("disable-reset"), 230 + { } 231 + }; 232 + 233 + static const struct software_node asus_tf103c_bq24190_node = { 234 + .properties = asus_tf103c_bq24190_props, 235 + }; 236 + 237 + static const struct property_entry asus_tf103c_ug3105_props[] = { 238 + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), 239 + PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), 240 + PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), 241 + { } 242 + }; 243 + 244 + static const struct software_node asus_tf103c_ug3105_node = { 245 + .properties = asus_tf103c_ug3105_props, 246 + }; 247 + 248 + static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = { 249 + { 250 + /* bq24297 battery charger */ 251 + .board_info = { 252 + .type = "bq24190", 253 + .addr = 0x6b, 254 + .dev_name = "bq24297", 255 + .swnode = &asus_tf103c_bq24190_node, 256 + .platform_data = &bq24190_pdata, 257 + }, 258 + .adapter_path = "\\_SB_.I2C1", 259 + .irq_data = { 260 + .type = X86_ACPI_IRQ_TYPE_PMIC, 261 + .chip = "\\_SB_.I2C7.PMIC", 262 + .domain = DOMAIN_BUS_WAKEUP, 263 + .index = 0, 264 + }, 265 + }, { 266 + /* ug3105 battery monitor */ 267 + .board_info = { 268 + .type = "ug3105", 269 + .addr = 0x70, 270 + .dev_name = "ug3105", 271 + .swnode = &asus_tf103c_ug3105_node, 272 + }, 273 + .adapter_path = "\\_SB_.I2C1", 274 + }, { 275 + /* ak09911 compass */ 276 + .board_info = { 277 + .type = "ak09911", 278 + .addr = 0x0c, 279 + .dev_name = "ak09911", 280 + }, 281 + .adapter_path = "\\_SB_.I2C5", 282 + }, { 283 + /* kxtj21009 accel */ 284 + .board_info = { 285 + .type = "kxtj21009", 286 + .addr = 0x0f, 287 + .dev_name = "kxtj21009", 288 + .swnode = &asus_tf103c_accel_node, 289 + }, 290 + .adapter_path = "\\_SB_.I2C5", 291 + }, { 292 + /* atmel touchscreen */ 293 + .board_info = { 294 + .type = "atmel_mxt_ts", 295 + .addr = 0x4a, 296 + .dev_name = "atmel_mxt_ts", 297 + .swnode = &asus_tf103c_touchscreen_node, 298 + }, 299 + .adapter_path = "\\_SB_.I2C6", 300 + .irq_data = { 301 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 302 + .chip = "INT33FC:02", 303 + .index = 28, 304 + .trigger = ACPI_EDGE_SENSITIVE, 305 + .polarity = ACPI_ACTIVE_LOW, 306 + }, 307 + }, 308 + }; 309 + 310 + static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { 311 + &int3496_gpo2_pin22_gpios, 312 + NULL 313 + }; 314 + 315 + const struct x86_dev_info asus_tf103c_info __initconst = { 316 + .i2c_client_info = asus_tf103c_i2c_clients, 317 + .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), 318 + .pdev_info = int3496_pdevs, 319 + .pdev_count = 1, 320 + .gpio_button = &asus_me176c_tf103c_lid, 321 + .gpiod_lookup_tables = asus_tf103c_gpios, 322 + .bat_swnode = &asus_tf103c_battery_node, 323 + .modules = bq24190_modules, 324 + .invalid_aei_gpiochip = "INT33FC:02", 325 + };
+391
drivers/platform/x86/x86-android-tablets/core.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * DMI based code to deal with broken DSDTs on X86 tablets which ship with 4 + * Android as (part of) the factory image. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 + 13 + #include <linux/acpi.h> 14 + #include <linux/dmi.h> 15 + #include <linux/gpio/driver.h> 16 + #include <linux/gpio/machine.h> 17 + #include <linux/irq.h> 18 + #include <linux/module.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/serdev.h> 21 + #include <linux/string.h> 22 + 23 + #include "x86-android-tablets.h" 24 + /* For gpiochip_get_desc() which is EXPORT_SYMBOL_GPL() */ 25 + #include "../../../gpio/gpiolib.h" 26 + #include "../../../gpio/gpiolib-acpi.h" 27 + 28 + static int gpiochip_find_match_label(struct gpio_chip *gc, void *data) 29 + { 30 + return gc->label && !strcmp(gc->label, data); 31 + } 32 + 33 + int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc) 34 + { 35 + struct gpio_desc *gpiod; 36 + struct gpio_chip *chip; 37 + 38 + chip = gpiochip_find((void *)label, gpiochip_find_match_label); 39 + if (!chip) { 40 + pr_err("error cannot find GPIO chip %s\n", label); 41 + return -ENODEV; 42 + } 43 + 44 + gpiod = gpiochip_get_desc(chip, pin); 45 + if (IS_ERR(gpiod)) { 46 + pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin); 47 + return PTR_ERR(gpiod); 48 + } 49 + 50 + *desc = gpiod; 51 + return 0; 52 + } 53 + 54 + int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) 55 + { 56 + struct irq_fwspec fwspec = { }; 57 + struct irq_domain *domain; 58 + struct acpi_device *adev; 59 + struct gpio_desc *gpiod; 60 + unsigned int irq_type; 61 + acpi_handle handle; 62 + acpi_status status; 63 + int irq, ret; 64 + 65 + switch (data->type) { 66 + case X86_ACPI_IRQ_TYPE_APIC: 67 + /* 68 + * The DSDT may already reference the GSI in a device skipped by 69 + * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI 70 + * to avoid EBUSY errors in this case. 71 + */ 72 + acpi_unregister_gsi(data->index); 73 + irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); 74 + if (irq < 0) 75 + pr_err("error %d getting APIC IRQ %d\n", irq, data->index); 76 + 77 + return irq; 78 + case X86_ACPI_IRQ_TYPE_GPIOINT: 79 + /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ 80 + ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod); 81 + if (ret) 82 + return ret; 83 + 84 + irq = gpiod_to_irq(gpiod); 85 + if (irq < 0) { 86 + pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); 87 + return irq; 88 + } 89 + 90 + irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); 91 + if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) 92 + irq_set_irq_type(irq, irq_type); 93 + 94 + return irq; 95 + case X86_ACPI_IRQ_TYPE_PMIC: 96 + status = acpi_get_handle(NULL, data->chip, &handle); 97 + if (ACPI_FAILURE(status)) { 98 + pr_err("error could not get %s handle\n", data->chip); 99 + return -ENODEV; 100 + } 101 + 102 + adev = acpi_fetch_acpi_dev(handle); 103 + if (!adev) { 104 + pr_err("error could not get %s adev\n", data->chip); 105 + return -ENODEV; 106 + } 107 + 108 + fwspec.fwnode = acpi_fwnode_handle(adev); 109 + domain = irq_find_matching_fwspec(&fwspec, data->domain); 110 + if (!domain) { 111 + pr_err("error could not find IRQ domain for %s\n", data->chip); 112 + return -ENODEV; 113 + } 114 + 115 + return irq_create_mapping(domain, data->index); 116 + default: 117 + return 0; 118 + } 119 + } 120 + 121 + static int i2c_client_count; 122 + static int pdev_count; 123 + static int serdev_count; 124 + static struct i2c_client **i2c_clients; 125 + static struct platform_device **pdevs; 126 + static struct serdev_device **serdevs; 127 + static struct gpiod_lookup_table * const *gpiod_lookup_tables; 128 + static const struct software_node *bat_swnode; 129 + static void (*exit_handler)(void); 130 + 131 + static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, 132 + int idx) 133 + { 134 + const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; 135 + struct i2c_board_info board_info = client_info->board_info; 136 + struct i2c_adapter *adap; 137 + acpi_handle handle; 138 + acpi_status status; 139 + 140 + board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); 141 + if (board_info.irq < 0) 142 + return board_info.irq; 143 + 144 + status = acpi_get_handle(NULL, client_info->adapter_path, &handle); 145 + if (ACPI_FAILURE(status)) { 146 + pr_err("Error could not get %s handle\n", client_info->adapter_path); 147 + return -ENODEV; 148 + } 149 + 150 + adap = i2c_acpi_find_adapter_by_handle(handle); 151 + if (!adap) { 152 + pr_err("error could not get %s adapter\n", client_info->adapter_path); 153 + return -ENODEV; 154 + } 155 + 156 + i2c_clients[idx] = i2c_new_client_device(adap, &board_info); 157 + put_device(&adap->dev); 158 + if (IS_ERR(i2c_clients[idx])) 159 + return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), 160 + "creating I2C-client %d\n", idx); 161 + 162 + return 0; 163 + } 164 + 165 + static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) 166 + { 167 + struct acpi_device *ctrl_adev, *serdev_adev; 168 + struct serdev_device *serdev; 169 + struct device *ctrl_dev; 170 + int ret = -ENODEV; 171 + 172 + ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); 173 + if (!ctrl_adev) { 174 + pr_err("error could not get %s/%s ctrl adev\n", 175 + info->ctrl_hid, info->ctrl_uid); 176 + return -ENODEV; 177 + } 178 + 179 + serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); 180 + if (!serdev_adev) { 181 + pr_err("error could not get %s serdev adev\n", info->serdev_hid); 182 + goto put_ctrl_adev; 183 + } 184 + 185 + /* get_first_physical_node() returns a weak ref, no need to put() it */ 186 + ctrl_dev = acpi_get_first_physical_node(ctrl_adev); 187 + if (!ctrl_dev) { 188 + pr_err("error could not get %s/%s ctrl physical dev\n", 189 + info->ctrl_hid, info->ctrl_uid); 190 + goto put_serdev_adev; 191 + } 192 + 193 + /* ctrl_dev now points to the controller's parent, get the controller */ 194 + ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); 195 + if (!ctrl_dev) { 196 + pr_err("error could not get %s/%s %s ctrl dev\n", 197 + info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); 198 + goto put_serdev_adev; 199 + } 200 + 201 + serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); 202 + if (!serdev) { 203 + ret = -ENOMEM; 204 + goto put_serdev_adev; 205 + } 206 + 207 + ACPI_COMPANION_SET(&serdev->dev, serdev_adev); 208 + acpi_device_set_enumerated(serdev_adev); 209 + 210 + ret = serdev_device_add(serdev); 211 + if (ret) { 212 + dev_err(&serdev->dev, "error %d adding serdev\n", ret); 213 + serdev_device_put(serdev); 214 + goto put_serdev_adev; 215 + } 216 + 217 + serdevs[idx] = serdev; 218 + 219 + put_serdev_adev: 220 + acpi_dev_put(serdev_adev); 221 + put_ctrl_adev: 222 + acpi_dev_put(ctrl_adev); 223 + return ret; 224 + } 225 + 226 + static void x86_android_tablet_cleanup(void) 227 + { 228 + int i; 229 + 230 + for (i = 0; i < serdev_count; i++) { 231 + if (serdevs[i]) 232 + serdev_device_remove(serdevs[i]); 233 + } 234 + 235 + kfree(serdevs); 236 + 237 + for (i = 0; i < pdev_count; i++) 238 + platform_device_unregister(pdevs[i]); 239 + 240 + kfree(pdevs); 241 + 242 + for (i = 0; i < i2c_client_count; i++) 243 + i2c_unregister_device(i2c_clients[i]); 244 + 245 + kfree(i2c_clients); 246 + 247 + if (exit_handler) 248 + exit_handler(); 249 + 250 + for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) 251 + gpiod_remove_lookup_table(gpiod_lookup_tables[i]); 252 + 253 + software_node_unregister(bat_swnode); 254 + } 255 + 256 + static __init int x86_android_tablet_init(void) 257 + { 258 + const struct x86_dev_info *dev_info; 259 + const struct dmi_system_id *id; 260 + struct gpio_chip *chip; 261 + int i, ret = 0; 262 + 263 + id = dmi_first_match(x86_android_tablet_ids); 264 + if (!id) 265 + return -ENODEV; 266 + 267 + dev_info = id->driver_data; 268 + 269 + /* 270 + * The broken DSDTs on these devices often also include broken 271 + * _AEI (ACPI Event Interrupt) handlers, disable these. 272 + */ 273 + if (dev_info->invalid_aei_gpiochip) { 274 + chip = gpiochip_find(dev_info->invalid_aei_gpiochip, 275 + gpiochip_find_match_label); 276 + if (!chip) { 277 + pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip); 278 + return -ENODEV; 279 + } 280 + acpi_gpiochip_free_interrupts(chip); 281 + } 282 + 283 + /* 284 + * Since this runs from module_init() it cannot use -EPROBE_DEFER, 285 + * instead pre-load any modules which are listed as requirements. 286 + */ 287 + for (i = 0; dev_info->modules && dev_info->modules[i]; i++) 288 + request_module(dev_info->modules[i]); 289 + 290 + bat_swnode = dev_info->bat_swnode; 291 + if (bat_swnode) { 292 + ret = software_node_register(bat_swnode); 293 + if (ret) 294 + return ret; 295 + } 296 + 297 + gpiod_lookup_tables = dev_info->gpiod_lookup_tables; 298 + for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) 299 + gpiod_add_lookup_table(gpiod_lookup_tables[i]); 300 + 301 + if (dev_info->init) { 302 + ret = dev_info->init(); 303 + if (ret < 0) { 304 + x86_android_tablet_cleanup(); 305 + return ret; 306 + } 307 + exit_handler = dev_info->exit; 308 + } 309 + 310 + i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); 311 + if (!i2c_clients) { 312 + x86_android_tablet_cleanup(); 313 + return -ENOMEM; 314 + } 315 + 316 + i2c_client_count = dev_info->i2c_client_count; 317 + for (i = 0; i < i2c_client_count; i++) { 318 + ret = x86_instantiate_i2c_client(dev_info, i); 319 + if (ret < 0) { 320 + x86_android_tablet_cleanup(); 321 + return ret; 322 + } 323 + } 324 + 325 + /* + 1 to make space for (optional) gpio_keys_button pdev */ 326 + pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL); 327 + if (!pdevs) { 328 + x86_android_tablet_cleanup(); 329 + return -ENOMEM; 330 + } 331 + 332 + pdev_count = dev_info->pdev_count; 333 + for (i = 0; i < pdev_count; i++) { 334 + pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); 335 + if (IS_ERR(pdevs[i])) { 336 + x86_android_tablet_cleanup(); 337 + return PTR_ERR(pdevs[i]); 338 + } 339 + } 340 + 341 + serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); 342 + if (!serdevs) { 343 + x86_android_tablet_cleanup(); 344 + return -ENOMEM; 345 + } 346 + 347 + serdev_count = dev_info->serdev_count; 348 + for (i = 0; i < serdev_count; i++) { 349 + ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); 350 + if (ret < 0) { 351 + x86_android_tablet_cleanup(); 352 + return ret; 353 + } 354 + } 355 + 356 + if (dev_info->gpio_button) { 357 + struct gpio_keys_platform_data pdata = { 358 + .buttons = &dev_info->gpio_button->button, 359 + .nbuttons = 1, 360 + }; 361 + struct gpio_desc *gpiod; 362 + 363 + /* Get GPIO for the gpio-button */ 364 + ret = x86_android_tablet_get_gpiod(dev_info->gpio_button->chip, 365 + dev_info->gpio_button->pin, &gpiod); 366 + if (ret < 0) { 367 + x86_android_tablet_cleanup(); 368 + return ret; 369 + } 370 + 371 + dev_info->gpio_button->button.gpio = desc_to_gpio(gpiod); 372 + 373 + pdevs[pdev_count] = platform_device_register_data(NULL, "gpio-keys", 374 + PLATFORM_DEVID_AUTO, 375 + &pdata, sizeof(pdata)); 376 + if (IS_ERR(pdevs[pdev_count])) { 377 + x86_android_tablet_cleanup(); 378 + return PTR_ERR(pdevs[pdev_count]); 379 + } 380 + pdev_count++; 381 + } 382 + 383 + return 0; 384 + } 385 + 386 + module_init(x86_android_tablet_init); 387 + module_exit(x86_android_tablet_cleanup); 388 + 389 + MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 390 + MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); 391 + MODULE_LICENSE("GPL");
+165
drivers/platform/x86/x86-android-tablets/dmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * DMI based code to deal with broken DSDTs on X86 tablets which ship with 4 + * Android as (part of) the factory image. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #include <linux/dmi.h> 12 + #include <linux/init.h> 13 + #include <linux/mod_devicetable.h> 14 + #include <linux/module.h> 15 + 16 + #include "x86-android-tablets.h" 17 + 18 + const struct dmi_system_id x86_android_tablet_ids[] __initconst = { 19 + { 20 + /* Acer Iconia One 7 B1-750 */ 21 + .matches = { 22 + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), 23 + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), 24 + }, 25 + .driver_data = (void *)&acer_b1_750_info, 26 + }, 27 + { 28 + /* Advantech MICA-071 */ 29 + .matches = { 30 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), 31 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), 32 + }, 33 + .driver_data = (void *)&advantech_mica_071_info, 34 + }, 35 + { 36 + /* Asus MeMO Pad 7 ME176C */ 37 + .matches = { 38 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 39 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), 40 + }, 41 + .driver_data = (void *)&asus_me176c_info, 42 + }, 43 + { 44 + /* Asus TF103C */ 45 + .matches = { 46 + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 47 + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), 48 + }, 49 + .driver_data = (void *)&asus_tf103c_info, 50 + }, 51 + { 52 + /* Chuwi Hi8 (CWI509) */ 53 + .matches = { 54 + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), 55 + DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"), 56 + DMI_MATCH(DMI_SYS_VENDOR, "ilife"), 57 + DMI_MATCH(DMI_PRODUCT_NAME, "S806"), 58 + }, 59 + .driver_data = (void *)&chuwi_hi8_info, 60 + }, 61 + { 62 + /* CZC P10T */ 63 + .ident = "CZC ODEON TPC-10 (\"P10T\")", 64 + .matches = { 65 + DMI_MATCH(DMI_SYS_VENDOR, "CZC"), 66 + DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"), 67 + }, 68 + .driver_data = (void *)&czc_p10t, 69 + }, 70 + { 71 + /* CZC P10T variant */ 72 + .ident = "ViewSonic ViewPad 10", 73 + .matches = { 74 + DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"), 75 + DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"), 76 + }, 77 + .driver_data = (void *)&czc_p10t, 78 + }, 79 + { 80 + /* Lenovo Yoga Book X90F / X90L */ 81 + .matches = { 82 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 83 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 84 + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), 85 + }, 86 + .driver_data = (void *)&lenovo_yogabook_x90_info, 87 + }, 88 + { 89 + /* Lenovo Yoga Book X91F / X91L */ 90 + .matches = { 91 + /* Non exact match to match F + L versions */ 92 + DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), 93 + }, 94 + .driver_data = (void *)&lenovo_yogabook_x91_info, 95 + }, 96 + { 97 + /* 98 + * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" 99 + * Lenovo Yoga Tablet 2 use the same mainboard) 100 + */ 101 + .matches = { 102 + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), 103 + DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), 104 + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), 105 + /* Partial match on beginning of BIOS version */ 106 + DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), 107 + }, 108 + .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, 109 + }, 110 + { 111 + /* Lenovo Yoga Tab 3 Pro YT3-X90F */ 112 + .matches = { 113 + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 114 + DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 115 + DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), 116 + }, 117 + .driver_data = (void *)&lenovo_yt3_info, 118 + }, 119 + { 120 + /* Medion Lifetab S10346 */ 121 + .matches = { 122 + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 123 + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), 124 + /* Above strings are much too generic, also match on BIOS date */ 125 + DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), 126 + }, 127 + .driver_data = (void *)&medion_lifetab_s10346_info, 128 + }, 129 + { 130 + /* Nextbook Ares 8 */ 131 + .matches = { 132 + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), 133 + DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), 134 + }, 135 + .driver_data = (void *)&nextbook_ares8_info, 136 + }, 137 + { 138 + /* Peaq C1010 */ 139 + .matches = { 140 + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), 141 + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), 142 + }, 143 + .driver_data = (void *)&peaq_c1010_info, 144 + }, 145 + { 146 + /* Whitelabel (sold as various brands) TM800A550L */ 147 + .matches = { 148 + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 149 + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), 150 + /* Above strings are too generic, also match on BIOS version */ 151 + DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), 152 + }, 153 + .driver_data = (void *)&whitelabel_tm800a550l_info, 154 + }, 155 + { 156 + /* Xiaomi Mi Pad 2 */ 157 + .matches = { 158 + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), 159 + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), 160 + }, 161 + .driver_data = (void *)&xiaomi_mipad2_info, 162 + }, 163 + { } 164 + }; 165 + MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids);
+679
drivers/platform/x86/x86-android-tablets/lenovo.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Board info for Lenovo X86 tablets which ship with Android as the factory image 4 + * and which have broken DSDT tables. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 + 13 + #include <linux/efi.h> 14 + #include <linux/gpio/machine.h> 15 + #include <linux/mfd/intel_soc_pmic.h> 16 + #include <linux/pinctrl/consumer.h> 17 + #include <linux/pinctrl/machine.h> 18 + #include <linux/platform_data/lp855x.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/reboot.h> 21 + #include <linux/rmi.h> 22 + #include <linux/spi/spi.h> 23 + 24 + #include "shared-psy-info.h" 25 + #include "x86-android-tablets.h" 26 + 27 + /* 28 + * Various Lenovo models use a TI LP8557 LED backlight controller with its PWM 29 + * input connected to a PWM output coming from the LCD panel's controller. 30 + * The Android kernels have a hack in the i915 driver to write a non-standard 31 + * panel specific DSI register to set the duty-cycle of the LCD's PWM output. 32 + * 33 + * To avoid having to have a similar hack in the mainline kernel program the 34 + * LP8557 to directly set the level and use the lp855x_bl driver for control. 35 + */ 36 + static struct lp855x_platform_data lenovo_lp8557_pdata = { 37 + .device_control = 0x86, 38 + .initial_brightness = 128, 39 + }; 40 + 41 + /* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ 42 + 43 + static const struct property_entry lenovo_yb1_x90_wacom_props[] = { 44 + PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001), 45 + PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 150), 46 + { } 47 + }; 48 + 49 + static const struct software_node lenovo_yb1_x90_wacom_node = { 50 + .properties = lenovo_yb1_x90_wacom_props, 51 + }; 52 + 53 + /* 54 + * The HiDeep IST940E touchscreen comes up in I2C-HID mode. The native protocol 55 + * reports ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR which are not reported in HID 56 + * mode, so using native mode is preferred. 57 + * It could alternatively be used in HID mode by changing the properties to: 58 + * PROPERTY_ENTRY_U32("hid-descr-addr", 0x0020), 59 + * PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120), 60 + * and changing board_info.type to "hid-over-i2c". 61 + */ 62 + static const struct property_entry lenovo_yb1_x90_hideep_ts_props[] = { 63 + PROPERTY_ENTRY_U32("touchscreen-size-x", 1200), 64 + PROPERTY_ENTRY_U32("touchscreen-size-y", 1920), 65 + PROPERTY_ENTRY_U32("touchscreen-max-pressure", 16384), 66 + PROPERTY_ENTRY_BOOL("hideep,force-native-protocol"), 67 + { } 68 + }; 69 + 70 + static const struct software_node lenovo_yb1_x90_hideep_ts_node = { 71 + .properties = lenovo_yb1_x90_hideep_ts_props, 72 + }; 73 + 74 + static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst = { 75 + { 76 + /* BQ27542 fuel-gauge */ 77 + .board_info = { 78 + .type = "bq27542", 79 + .addr = 0x55, 80 + .dev_name = "bq27542", 81 + .swnode = &fg_bq25890_supply_node, 82 + }, 83 + .adapter_path = "\\_SB_.PCI0.I2C1", 84 + }, { 85 + /* Goodix Touchscreen in keyboard half */ 86 + .board_info = { 87 + .type = "GDIX1001:00", 88 + .addr = 0x14, 89 + .dev_name = "goodix_ts", 90 + }, 91 + .adapter_path = "\\_SB_.PCI0.I2C2", 92 + .irq_data = { 93 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 94 + .chip = "INT33FF:01", 95 + .index = 56, 96 + .trigger = ACPI_EDGE_SENSITIVE, 97 + .polarity = ACPI_ACTIVE_LOW, 98 + }, 99 + }, { 100 + /* Wacom Digitizer in keyboard half */ 101 + .board_info = { 102 + .type = "hid-over-i2c", 103 + .addr = 0x09, 104 + .dev_name = "wacom", 105 + .swnode = &lenovo_yb1_x90_wacom_node, 106 + }, 107 + .adapter_path = "\\_SB_.PCI0.I2C4", 108 + .irq_data = { 109 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 110 + .chip = "INT33FF:01", 111 + .index = 49, 112 + .trigger = ACPI_LEVEL_SENSITIVE, 113 + .polarity = ACPI_ACTIVE_LOW, 114 + }, 115 + }, { 116 + /* LP8557 Backlight controller */ 117 + .board_info = { 118 + .type = "lp8557", 119 + .addr = 0x2c, 120 + .dev_name = "lp8557", 121 + .platform_data = &lenovo_lp8557_pdata, 122 + }, 123 + .adapter_path = "\\_SB_.PCI0.I2C4", 124 + }, { 125 + /* HiDeep IST940E Touchscreen in display half */ 126 + .board_info = { 127 + .type = "hideep_ts", 128 + .addr = 0x6c, 129 + .dev_name = "hideep_ts", 130 + .swnode = &lenovo_yb1_x90_hideep_ts_node, 131 + }, 132 + .adapter_path = "\\_SB_.PCI0.I2C6", 133 + .irq_data = { 134 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 135 + .chip = "INT33FF:03", 136 + .index = 77, 137 + .trigger = ACPI_LEVEL_SENSITIVE, 138 + .polarity = ACPI_ACTIVE_LOW, 139 + }, 140 + }, 141 + }; 142 + 143 + static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = { 144 + { 145 + .name = "yogabook-touch-kbd-digitizer-switch", 146 + .id = PLATFORM_DEVID_NONE, 147 + }, 148 + }; 149 + 150 + static struct gpiod_lookup_table lenovo_yb1_x90_goodix_gpios = { 151 + .dev_id = "i2c-goodix_ts", 152 + .table = { 153 + GPIO_LOOKUP("INT33FF:01", 53, "reset", GPIO_ACTIVE_HIGH), 154 + GPIO_LOOKUP("INT33FF:01", 56, "irq", GPIO_ACTIVE_HIGH), 155 + { } 156 + }, 157 + }; 158 + 159 + static struct gpiod_lookup_table lenovo_yb1_x90_hideep_gpios = { 160 + .dev_id = "i2c-hideep_ts", 161 + .table = { 162 + GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW), 163 + { } 164 + }, 165 + }; 166 + 167 + static struct gpiod_lookup_table lenovo_yb1_x90_wacom_gpios = { 168 + .dev_id = "i2c-wacom", 169 + .table = { 170 + GPIO_LOOKUP("INT33FF:00", 82, "reset", GPIO_ACTIVE_LOW), 171 + { } 172 + }, 173 + }; 174 + 175 + static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = { 176 + &lenovo_yb1_x90_hideep_gpios, 177 + &lenovo_yb1_x90_goodix_gpios, 178 + &lenovo_yb1_x90_wacom_gpios, 179 + NULL 180 + }; 181 + 182 + static int __init lenovo_yb1_x90_init(void) 183 + { 184 + /* Enable the regulators used by the touchscreens */ 185 + 186 + /* Vprog3B 3.0V used by the goodix touchscreen in the keyboard half */ 187 + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9b, 0x02, 0xff); 188 + 189 + /* Vprog4D 3.0V used by the HiDeep touchscreen in the display half */ 190 + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9f, 0x02, 0xff); 191 + 192 + /* Vprog5A 1.8V used by the HiDeep touchscreen in the display half */ 193 + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa0, 0x02, 0xff); 194 + 195 + /* Vprog5B 1.8V used by the goodix touchscreen in the keyboard half */ 196 + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa1, 0x02, 0xff); 197 + 198 + return 0; 199 + } 200 + 201 + const struct x86_dev_info lenovo_yogabook_x90_info __initconst = { 202 + .i2c_client_info = lenovo_yb1_x90_i2c_clients, 203 + .i2c_client_count = ARRAY_SIZE(lenovo_yb1_x90_i2c_clients), 204 + .pdev_info = lenovo_yb1_x90_pdevs, 205 + .pdev_count = ARRAY_SIZE(lenovo_yb1_x90_pdevs), 206 + .gpiod_lookup_tables = lenovo_yb1_x90_gpios, 207 + .init = lenovo_yb1_x90_init, 208 + }; 209 + 210 + /* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */ 211 + static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = { 212 + { 213 + /* BQ27542 fuel-gauge */ 214 + .board_info = { 215 + .type = "bq27542", 216 + .addr = 0x55, 217 + .dev_name = "bq27542", 218 + .swnode = &fg_bq25890_supply_node, 219 + }, 220 + .adapter_path = "\\_SB_.PCI0.I2C1", 221 + }, 222 + }; 223 + 224 + const struct x86_dev_info lenovo_yogabook_x91_info __initconst = { 225 + .i2c_client_info = lenovo_yogabook_x91_i2c_clients, 226 + .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x91_i2c_clients), 227 + }; 228 + 229 + /* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ 230 + static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { 231 + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), 232 + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 233 + PROPERTY_ENTRY_BOOL("omit-battery-class"), 234 + PROPERTY_ENTRY_BOOL("disable-reset"), 235 + { } 236 + }; 237 + 238 + static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { 239 + .properties = lenovo_yoga_tab2_830_1050_bq24190_props, 240 + }; 241 + 242 + static struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid = { 243 + .button = { 244 + .code = SW_LID, 245 + .active_low = true, 246 + .desc = "lid_sw", 247 + .type = EV_SW, 248 + .wakeup = true, 249 + .debounce_interval = 50, 250 + }, 251 + .chip = "INT33FC:02", 252 + .pin = 26, 253 + }; 254 + 255 + /* This gets filled by lenovo_yoga_tab2_830_1050_init() */ 256 + static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; 257 + 258 + static struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initdata = { 259 + { 260 + /* 261 + * This must be the first entry because lenovo_yoga_tab2_830_1050_init() 262 + * may update its swnode. LSM303DA accelerometer + magnetometer. 263 + */ 264 + .board_info = { 265 + .type = "lsm303d", 266 + .addr = 0x1d, 267 + .dev_name = "lsm303d", 268 + }, 269 + .adapter_path = "\\_SB_.I2C5", 270 + }, { 271 + /* bq24292i battery charger */ 272 + .board_info = { 273 + .type = "bq24190", 274 + .addr = 0x6b, 275 + .dev_name = "bq24292i", 276 + .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, 277 + .platform_data = &bq24190_pdata, 278 + }, 279 + .adapter_path = "\\_SB_.I2C1", 280 + .irq_data = { 281 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 282 + .chip = "INT33FC:02", 283 + .index = 2, 284 + .trigger = ACPI_EDGE_SENSITIVE, 285 + .polarity = ACPI_ACTIVE_HIGH, 286 + }, 287 + }, { 288 + /* BQ27541 fuel-gauge */ 289 + .board_info = { 290 + .type = "bq27541", 291 + .addr = 0x55, 292 + .dev_name = "bq27541", 293 + .swnode = &fg_bq24190_supply_node, 294 + }, 295 + .adapter_path = "\\_SB_.I2C1", 296 + }, { 297 + /* Synaptics RMI touchscreen */ 298 + .board_info = { 299 + .type = "rmi4_i2c", 300 + .addr = 0x38, 301 + .dev_name = "rmi4_i2c", 302 + .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, 303 + }, 304 + .adapter_path = "\\_SB_.I2C6", 305 + .irq_data = { 306 + .type = X86_ACPI_IRQ_TYPE_APIC, 307 + .index = 0x45, 308 + .trigger = ACPI_EDGE_SENSITIVE, 309 + .polarity = ACPI_ACTIVE_HIGH, 310 + }, 311 + }, { 312 + /* LP8557 Backlight controller */ 313 + .board_info = { 314 + .type = "lp8557", 315 + .addr = 0x2c, 316 + .dev_name = "lp8557", 317 + .platform_data = &lenovo_lp8557_pdata, 318 + }, 319 + .adapter_path = "\\_SB_.I2C3", 320 + }, 321 + }; 322 + 323 + static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { 324 + .dev_id = "intel-int3496", 325 + .table = { 326 + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW), 327 + GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH), 328 + { } 329 + }, 330 + }; 331 + 332 + #define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" 333 + 334 + static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { 335 + .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, 336 + .table = { 337 + GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH), 338 + GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH), 339 + GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), 340 + GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW), 341 + { } 342 + }, 343 + }; 344 + 345 + static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { 346 + &lenovo_yoga_tab2_830_1050_int3496_gpios, 347 + &lenovo_yoga_tab2_830_1050_codec_gpios, 348 + NULL 349 + }; 350 + 351 + static int __init lenovo_yoga_tab2_830_1050_init(void); 352 + static void lenovo_yoga_tab2_830_1050_exit(void); 353 + 354 + const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { 355 + .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, 356 + .i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients), 357 + .pdev_info = int3496_pdevs, 358 + .pdev_count = 1, 359 + .gpio_button = &lenovo_yoga_tab2_830_1050_lid, 360 + .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, 361 + .bat_swnode = &generic_lipo_hv_4v35_battery_node, 362 + .modules = bq24190_modules, 363 + .init = lenovo_yoga_tab2_830_1050_init, 364 + .exit = lenovo_yoga_tab2_830_1050_exit, 365 + }; 366 + 367 + /* 368 + * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same 369 + * mainboard, but the 830 uses a portrait LCD panel with a landscape touchscreen, 370 + * requiring the touchscreen driver to adjust the touch-coords to match the LCD. 371 + * And requiring the accelerometer to have a mount-matrix set to correct for 372 + * the 90° rotation of the LCD vs the frame. 373 + */ 374 + static const char * const lenovo_yoga_tab2_830_lms303d_mount_matrix[] = { 375 + "0", "1", "0", 376 + "-1", "0", "0", 377 + "0", "0", "1" 378 + }; 379 + 380 + static const struct property_entry lenovo_yoga_tab2_830_lms303d_props[] = { 381 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", lenovo_yoga_tab2_830_lms303d_mount_matrix), 382 + { } 383 + }; 384 + 385 + static const struct software_node lenovo_yoga_tab2_830_lms303d_node = { 386 + .properties = lenovo_yoga_tab2_830_lms303d_props, 387 + }; 388 + 389 + static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void) 390 + { 391 + struct gpio_desc *gpiod; 392 + int ret; 393 + 394 + /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ 395 + ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod); 396 + if (ret) 397 + return ret; 398 + 399 + ret = gpiod_get_value_cansleep(gpiod); 400 + if (ret) { 401 + pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); 402 + } else { 403 + pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); 404 + lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; 405 + lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; 406 + lenovo_yoga_tab2_830_1050_i2c_clients[0].board_info.swnode = 407 + &lenovo_yoga_tab2_830_lms303d_node; 408 + } 409 + 410 + return 0; 411 + } 412 + 413 + /* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ 414 + static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = 415 + PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk", 416 + "INT33FC:02", "pmu_clk2_grp", "pmu_clk"); 417 + 418 + static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; 419 + static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; 420 + 421 + static int __init lenovo_yoga_tab2_830_1050_init_codec(void) 422 + { 423 + struct device *codec_dev; 424 + struct pinctrl *pinctrl; 425 + int ret; 426 + 427 + codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, 428 + LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); 429 + if (!codec_dev) { 430 + pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); 431 + return -ENODEV; 432 + } 433 + 434 + ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1); 435 + if (ret) 436 + goto err_put_device; 437 + 438 + pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk"); 439 + if (IS_ERR(pinctrl)) { 440 + ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n"); 441 + goto err_unregister_mappings; 442 + } 443 + 444 + /* We're done with the codec_dev now */ 445 + put_device(codec_dev); 446 + 447 + lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; 448 + return 0; 449 + 450 + err_unregister_mappings: 451 + pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); 452 + err_put_device: 453 + put_device(codec_dev); 454 + return ret; 455 + } 456 + 457 + /* 458 + * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off 459 + * gets used as pm_power_off handler. This causes "poweroff" on these tablets 460 + * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* 461 + * followed by a normal 3 second press to recover. Avoid this by doing an EFI 462 + * poweroff instead. 463 + */ 464 + static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) 465 + { 466 + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); 467 + 468 + return NOTIFY_DONE; 469 + } 470 + 471 + static int __init lenovo_yoga_tab2_830_1050_init(void) 472 + { 473 + int ret; 474 + 475 + ret = lenovo_yoga_tab2_830_1050_init_touchscreen(); 476 + if (ret) 477 + return ret; 478 + 479 + ret = lenovo_yoga_tab2_830_1050_init_codec(); 480 + if (ret) 481 + return ret; 482 + 483 + /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ 484 + lenovo_yoga_tab2_830_1050_sys_off_handler = 485 + register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, 486 + lenovo_yoga_tab2_830_1050_power_off, NULL); 487 + if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler)) 488 + return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler); 489 + 490 + return 0; 491 + } 492 + 493 + static void lenovo_yoga_tab2_830_1050_exit(void) 494 + { 495 + unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler); 496 + 497 + if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { 498 + pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl); 499 + pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); 500 + } 501 + } 502 + 503 + /* Lenovo Yoga Tab 3 Pro YT3-X90F */ 504 + 505 + /* 506 + * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, 507 + * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. 508 + */ 509 + static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; 510 + static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; 511 + 512 + static const struct property_entry fg_bq25890_1_supply_props[] = { 513 + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), 514 + { } 515 + }; 516 + 517 + static const struct software_node fg_bq25890_1_supply_node = { 518 + .properties = fg_bq25890_1_supply_props, 519 + }; 520 + 521 + /* bq25892 charger settings for the flat lipo battery behind the screen */ 522 + static const struct property_entry lenovo_yt3_bq25892_0_props[] = { 523 + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), 524 + PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), 525 + PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), 526 + PROPERTY_ENTRY_BOOL("linux,skip-reset"), 527 + /* Values taken from Android Factory Image */ 528 + PROPERTY_ENTRY_U32("ti,charge-current", 2048000), 529 + PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), 530 + PROPERTY_ENTRY_U32("ti,termination-current", 128000), 531 + PROPERTY_ENTRY_U32("ti,precharge-current", 128000), 532 + PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), 533 + PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), 534 + PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), 535 + PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), 536 + { } 537 + }; 538 + 539 + static const struct software_node lenovo_yt3_bq25892_0_node = { 540 + .properties = lenovo_yt3_bq25892_0_props, 541 + }; 542 + 543 + static const struct property_entry lenovo_yt3_hideep_ts_props[] = { 544 + PROPERTY_ENTRY_U32("touchscreen-size-x", 1600), 545 + PROPERTY_ENTRY_U32("touchscreen-size-y", 2560), 546 + PROPERTY_ENTRY_U32("touchscreen-max-pressure", 255), 547 + { } 548 + }; 549 + 550 + static const struct software_node lenovo_yt3_hideep_ts_node = { 551 + .properties = lenovo_yt3_hideep_ts_props, 552 + }; 553 + 554 + static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { 555 + { 556 + /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ 557 + .board_info = { 558 + .type = "bq27500", 559 + .addr = 0x55, 560 + .dev_name = "bq27500_0", 561 + .swnode = &fg_bq25890_supply_node, 562 + }, 563 + .adapter_path = "\\_SB_.PCI0.I2C1", 564 + }, { 565 + /* bq25892 charger for the flat lipo battery behind the screen */ 566 + .board_info = { 567 + .type = "bq25892", 568 + .addr = 0x6b, 569 + .dev_name = "bq25892_0", 570 + .swnode = &lenovo_yt3_bq25892_0_node, 571 + }, 572 + .adapter_path = "\\_SB_.PCI0.I2C1", 573 + .irq_data = { 574 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 575 + .chip = "INT33FF:01", 576 + .index = 5, 577 + .trigger = ACPI_EDGE_SENSITIVE, 578 + .polarity = ACPI_ACTIVE_LOW, 579 + }, 580 + }, { 581 + /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ 582 + .board_info = { 583 + .type = "bq27500", 584 + .addr = 0x55, 585 + .dev_name = "bq27500_1", 586 + .swnode = &fg_bq25890_1_supply_node, 587 + }, 588 + .adapter_path = "\\_SB_.PCI0.I2C2", 589 + }, { 590 + /* HiDeep IST520E Touchscreen */ 591 + .board_info = { 592 + .type = "hideep_ts", 593 + .addr = 0x6c, 594 + .dev_name = "hideep_ts", 595 + .swnode = &lenovo_yt3_hideep_ts_node, 596 + }, 597 + .adapter_path = "\\_SB_.PCI0.I2C6", 598 + .irq_data = { 599 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 600 + .chip = "INT33FF:03", 601 + .index = 77, 602 + .trigger = ACPI_LEVEL_SENSITIVE, 603 + .polarity = ACPI_ACTIVE_LOW, 604 + }, 605 + }, { 606 + /* LP8557 Backlight controller */ 607 + .board_info = { 608 + .type = "lp8557", 609 + .addr = 0x2c, 610 + .dev_name = "lp8557", 611 + .platform_data = &lenovo_lp8557_pdata, 612 + }, 613 + .adapter_path = "\\_SB_.PCI0.I2C1", 614 + } 615 + }; 616 + 617 + static int __init lenovo_yt3_init(void) 618 + { 619 + struct gpio_desc *gpiod; 620 + int ret; 621 + 622 + /* 623 + * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins 624 + * connected to GPIOs, rather then having them hardwired to the correct 625 + * values as is normally done. 626 + * 627 + * The bq25890_charger driver controls these through I2C, but this only 628 + * works if not overridden by the pins. Set these pins here: 629 + * 1. Set /CE to 0 to allow charging. 630 + * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of 631 + * the main "bq25892_1" charger is used when necessary. 632 + */ 633 + 634 + /* /CE pin */ 635 + ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); 636 + if (ret < 0) 637 + return ret; 638 + 639 + /* 640 + * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" 641 + * gpio_desc, that is there is no way to pass lookup-flags like 642 + * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since 643 + * the /CE pin is active-low, but not marked as such in the gpio_desc. 644 + */ 645 + gpiod_set_value(gpiod, 0); 646 + 647 + /* OTG pin */ 648 + ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); 649 + if (ret < 0) 650 + return ret; 651 + 652 + gpiod_set_value(gpiod, 0); 653 + 654 + /* Enable the regulators used by the touchscreen */ 655 + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9b, 0x02, 0xff); 656 + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa0, 0x02, 0xff); 657 + 658 + return 0; 659 + } 660 + 661 + static struct gpiod_lookup_table lenovo_yt3_hideep_gpios = { 662 + .dev_id = "i2c-hideep_ts", 663 + .table = { 664 + GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW), 665 + { } 666 + }, 667 + }; 668 + 669 + static struct gpiod_lookup_table * const lenovo_yt3_gpios[] = { 670 + &lenovo_yt3_hideep_gpios, 671 + NULL 672 + }; 673 + 674 + const struct x86_dev_info lenovo_yt3_info __initconst = { 675 + .i2c_client_info = lenovo_yt3_i2c_clients, 676 + .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), 677 + .gpiod_lookup_tables = lenovo_yt3_gpios, 678 + .init = lenovo_yt3_init, 679 + };
+522
drivers/platform/x86/x86-android-tablets/other.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * DMI based code to deal with broken DSDTs on X86 tablets which ship with 4 + * Android as (part of) the factory image. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #include <linux/acpi.h> 12 + #include <linux/gpio/machine.h> 13 + #include <linux/input.h> 14 + #include <linux/platform_device.h> 15 + 16 + #include "shared-psy-info.h" 17 + #include "x86-android-tablets.h" 18 + 19 + /* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ 20 + static const char * const acer_b1_750_mount_matrix[] = { 21 + "-1", "0", "0", 22 + "0", "1", "0", 23 + "0", "0", "1" 24 + }; 25 + 26 + static const struct property_entry acer_b1_750_bma250e_props[] = { 27 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), 28 + { } 29 + }; 30 + 31 + static const struct software_node acer_b1_750_bma250e_node = { 32 + .properties = acer_b1_750_bma250e_props, 33 + }; 34 + 35 + static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { 36 + { 37 + /* Novatek NVT-ts touchscreen */ 38 + .board_info = { 39 + .type = "NVT-ts", 40 + .addr = 0x34, 41 + .dev_name = "NVT-ts", 42 + }, 43 + .adapter_path = "\\_SB_.I2C4", 44 + .irq_data = { 45 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 46 + .chip = "INT33FC:02", 47 + .index = 3, 48 + .trigger = ACPI_EDGE_SENSITIVE, 49 + .polarity = ACPI_ACTIVE_LOW, 50 + }, 51 + }, { 52 + /* BMA250E accelerometer */ 53 + .board_info = { 54 + .type = "bma250e", 55 + .addr = 0x18, 56 + .swnode = &acer_b1_750_bma250e_node, 57 + }, 58 + .adapter_path = "\\_SB_.I2C3", 59 + .irq_data = { 60 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 61 + .chip = "INT33FC:02", 62 + .index = 25, 63 + .trigger = ACPI_LEVEL_SENSITIVE, 64 + .polarity = ACPI_ACTIVE_HIGH, 65 + }, 66 + }, 67 + }; 68 + 69 + static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { 70 + .dev_id = "i2c-NVT-ts", 71 + .table = { 72 + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), 73 + { } 74 + }, 75 + }; 76 + 77 + static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { 78 + &acer_b1_750_goodix_gpios, 79 + &int3496_reference_gpios, 80 + NULL 81 + }; 82 + 83 + const struct x86_dev_info acer_b1_750_info __initconst = { 84 + .i2c_client_info = acer_b1_750_i2c_clients, 85 + .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), 86 + .pdev_info = int3496_pdevs, 87 + .pdev_count = 1, 88 + .gpiod_lookup_tables = acer_b1_750_gpios, 89 + }; 90 + 91 + /* 92 + * Advantech MICA-071 93 + * This is a standard Windows tablet, but it has an extra "quick launch" button 94 + * which is not described in the ACPI tables in anyway. 95 + * Use the x86-android-tablets infra to create a gpio-button device for this. 96 + */ 97 + static struct x86_gpio_button advantech_mica_071_button = { 98 + .button = { 99 + .code = KEY_PROG1, 100 + .active_low = true, 101 + .desc = "prog1_key", 102 + .type = EV_KEY, 103 + .wakeup = false, 104 + .debounce_interval = 50, 105 + }, 106 + .chip = "INT33FC:00", 107 + .pin = 2, 108 + }; 109 + 110 + const struct x86_dev_info advantech_mica_071_info __initconst = { 111 + .gpio_button = &advantech_mica_071_button, 112 + }; 113 + 114 + /* 115 + * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT 116 + * contains a whole bunch of bogus ACPI I2C devices and is missing entries 117 + * for the touchscreen and the accelerometer. 118 + */ 119 + static const struct property_entry chuwi_hi8_gsl1680_props[] = { 120 + PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), 121 + PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), 122 + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 123 + PROPERTY_ENTRY_BOOL("silead,home-button"), 124 + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), 125 + { } 126 + }; 127 + 128 + static const struct software_node chuwi_hi8_gsl1680_node = { 129 + .properties = chuwi_hi8_gsl1680_props, 130 + }; 131 + 132 + static const char * const chuwi_hi8_mount_matrix[] = { 133 + "1", "0", "0", 134 + "0", "-1", "0", 135 + "0", "0", "1" 136 + }; 137 + 138 + static const struct property_entry chuwi_hi8_bma250e_props[] = { 139 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), 140 + { } 141 + }; 142 + 143 + static const struct software_node chuwi_hi8_bma250e_node = { 144 + .properties = chuwi_hi8_bma250e_props, 145 + }; 146 + 147 + static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { 148 + { 149 + /* Silead touchscreen */ 150 + .board_info = { 151 + .type = "gsl1680", 152 + .addr = 0x40, 153 + .swnode = &chuwi_hi8_gsl1680_node, 154 + }, 155 + .adapter_path = "\\_SB_.I2C4", 156 + .irq_data = { 157 + .type = X86_ACPI_IRQ_TYPE_APIC, 158 + .index = 0x44, 159 + .trigger = ACPI_EDGE_SENSITIVE, 160 + .polarity = ACPI_ACTIVE_HIGH, 161 + }, 162 + }, { 163 + /* BMA250E accelerometer */ 164 + .board_info = { 165 + .type = "bma250e", 166 + .addr = 0x18, 167 + .swnode = &chuwi_hi8_bma250e_node, 168 + }, 169 + .adapter_path = "\\_SB_.I2C3", 170 + .irq_data = { 171 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 172 + .chip = "INT33FC:02", 173 + .index = 23, 174 + .trigger = ACPI_LEVEL_SENSITIVE, 175 + .polarity = ACPI_ACTIVE_HIGH, 176 + }, 177 + }, 178 + }; 179 + 180 + static int __init chuwi_hi8_init(void) 181 + { 182 + /* 183 + * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() 184 + * breaking the touchscreen + logging various errors when the Windows 185 + * BIOS is used. 186 + */ 187 + if (acpi_dev_present("MSSL0001", NULL, 1)) 188 + return -ENODEV; 189 + 190 + return 0; 191 + } 192 + 193 + const struct x86_dev_info chuwi_hi8_info __initconst = { 194 + .i2c_client_info = chuwi_hi8_i2c_clients, 195 + .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), 196 + .init = chuwi_hi8_init, 197 + }; 198 + 199 + #define CZC_EC_EXTRA_PORT 0x68 200 + #define CZC_EC_ANDROID_KEYS 0x63 201 + 202 + static int __init czc_p10t_init(void) 203 + { 204 + /* 205 + * The device boots up in "Windows 7" mode, when the home button sends a 206 + * Windows specific key sequence (Left Meta + D) and the second button 207 + * sends an unknown one while also toggling the Radio Kill Switch. 208 + * This is a surprising behavior when the second button is labeled "Back". 209 + * 210 + * The vendor-supplied Android-x86 build switches the device to a "Android" 211 + * mode by writing value 0x63 to the I/O port 0x68. This just seems to just 212 + * set bit 6 on address 0x96 in the EC region; switching the bit directly 213 + * seems to achieve the same result. It uses a "p10t_switcher" to do the 214 + * job. It doesn't seem to be able to do anything else, and no other use 215 + * of the port 0x68 is known. 216 + * 217 + * In the Android mode, the home button sends just a single scancode, 218 + * which can be handled in Linux userspace more reasonably and the back 219 + * button only sends a scancode without toggling the kill switch. 220 + * The scancode can then be mapped either to Back or RF Kill functionality 221 + * in userspace, depending on how the button is labeled on that particular 222 + * model. 223 + */ 224 + outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); 225 + return 0; 226 + } 227 + 228 + const struct x86_dev_info czc_p10t __initconst = { 229 + .init = czc_p10t_init, 230 + }; 231 + 232 + /* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ 233 + static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { 234 + "0", "1", "0", 235 + "1", "0", "0", 236 + "0", "0", "1" 237 + }; 238 + 239 + static const struct property_entry medion_lifetab_s10346_accel_props[] = { 240 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), 241 + { } 242 + }; 243 + 244 + static const struct software_node medion_lifetab_s10346_accel_node = { 245 + .properties = medion_lifetab_s10346_accel_props, 246 + }; 247 + 248 + /* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ 249 + static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { 250 + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), 251 + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 252 + { } 253 + }; 254 + 255 + static const struct software_node medion_lifetab_s10346_touchscreen_node = { 256 + .properties = medion_lifetab_s10346_touchscreen_props, 257 + }; 258 + 259 + static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { 260 + { 261 + /* kxtj21009 accel */ 262 + .board_info = { 263 + .type = "kxtj21009", 264 + .addr = 0x0f, 265 + .dev_name = "kxtj21009", 266 + .swnode = &medion_lifetab_s10346_accel_node, 267 + }, 268 + .adapter_path = "\\_SB_.I2C3", 269 + .irq_data = { 270 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 271 + .chip = "INT33FC:02", 272 + .index = 23, 273 + .trigger = ACPI_EDGE_SENSITIVE, 274 + .polarity = ACPI_ACTIVE_HIGH, 275 + }, 276 + }, { 277 + /* goodix touchscreen */ 278 + .board_info = { 279 + .type = "GDIX1001:00", 280 + .addr = 0x14, 281 + .dev_name = "goodix_ts", 282 + .swnode = &medion_lifetab_s10346_touchscreen_node, 283 + }, 284 + .adapter_path = "\\_SB_.I2C4", 285 + .irq_data = { 286 + .type = X86_ACPI_IRQ_TYPE_APIC, 287 + .index = 0x44, 288 + .trigger = ACPI_EDGE_SENSITIVE, 289 + .polarity = ACPI_ACTIVE_LOW, 290 + }, 291 + }, 292 + }; 293 + 294 + static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { 295 + .dev_id = "i2c-goodix_ts", 296 + .table = { 297 + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), 298 + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), 299 + { } 300 + }, 301 + }; 302 + 303 + static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { 304 + &medion_lifetab_s10346_goodix_gpios, 305 + NULL 306 + }; 307 + 308 + const struct x86_dev_info medion_lifetab_s10346_info __initconst = { 309 + .i2c_client_info = medion_lifetab_s10346_i2c_clients, 310 + .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), 311 + .gpiod_lookup_tables = medion_lifetab_s10346_gpios, 312 + }; 313 + 314 + /* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ 315 + static const char * const nextbook_ares8_accel_mount_matrix[] = { 316 + "0", "-1", "0", 317 + "-1", "0", "0", 318 + "0", "0", "1" 319 + }; 320 + 321 + static const struct property_entry nextbook_ares8_accel_props[] = { 322 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), 323 + { } 324 + }; 325 + 326 + static const struct software_node nextbook_ares8_accel_node = { 327 + .properties = nextbook_ares8_accel_props, 328 + }; 329 + 330 + static const struct property_entry nextbook_ares8_touchscreen_props[] = { 331 + PROPERTY_ENTRY_U32("touchscreen-size-x", 800), 332 + PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), 333 + { } 334 + }; 335 + 336 + static const struct software_node nextbook_ares8_touchscreen_node = { 337 + .properties = nextbook_ares8_touchscreen_props, 338 + }; 339 + 340 + static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { 341 + { 342 + /* Freescale MMA8653FC accel */ 343 + .board_info = { 344 + .type = "mma8653", 345 + .addr = 0x1d, 346 + .dev_name = "mma8653", 347 + .swnode = &nextbook_ares8_accel_node, 348 + }, 349 + .adapter_path = "\\_SB_.I2C3", 350 + }, { 351 + /* FT5416DQ9 touchscreen controller */ 352 + .board_info = { 353 + .type = "edt-ft5x06", 354 + .addr = 0x38, 355 + .dev_name = "ft5416", 356 + .swnode = &nextbook_ares8_touchscreen_node, 357 + }, 358 + .adapter_path = "\\_SB_.I2C4", 359 + .irq_data = { 360 + .type = X86_ACPI_IRQ_TYPE_GPIOINT, 361 + .chip = "INT33FC:02", 362 + .index = 3, 363 + .trigger = ACPI_EDGE_SENSITIVE, 364 + .polarity = ACPI_ACTIVE_LOW, 365 + }, 366 + }, 367 + }; 368 + 369 + static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { 370 + &int3496_reference_gpios, 371 + NULL 372 + }; 373 + 374 + const struct x86_dev_info nextbook_ares8_info __initconst = { 375 + .i2c_client_info = nextbook_ares8_i2c_clients, 376 + .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), 377 + .pdev_info = int3496_pdevs, 378 + .pdev_count = 1, 379 + .gpiod_lookup_tables = nextbook_ares8_gpios, 380 + .invalid_aei_gpiochip = "INT33FC:02", 381 + }; 382 + 383 + /* 384 + * Peaq C1010 385 + * This is a standard Windows tablet, but it has a special Dolby button. 386 + * This button has a WMI interface, but that is broken. Instead of trying to 387 + * use the broken WMI interface, instantiate a gpio_keys device for this. 388 + */ 389 + static struct x86_gpio_button peaq_c1010_button = { 390 + .button = { 391 + .code = KEY_SOUND, 392 + .active_low = true, 393 + .desc = "dolby_key", 394 + .type = EV_KEY, 395 + .wakeup = false, 396 + .debounce_interval = 50, 397 + }, 398 + .chip = "INT33FC:00", 399 + .pin = 3, 400 + }; 401 + 402 + const struct x86_dev_info peaq_c1010_info __initconst = { 403 + .gpio_button = &peaq_c1010_button, 404 + /* 405 + * Move the ACPI event handler used by the broken WMI interface out of 406 + * the way. This is the only event handler on INT33FC:00. 407 + */ 408 + .invalid_aei_gpiochip = "INT33FC:00", 409 + }; 410 + 411 + /* 412 + * Whitelabel (sold as various brands) TM800A550L tablets. 413 + * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices 414 + * (removed through acpi_quirk_skip_i2c_client_enumeration()) and 415 + * the touchscreen fwnode has the wrong GPIOs. 416 + */ 417 + static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { 418 + "-1", "0", "0", 419 + "0", "1", "0", 420 + "0", "0", "1" 421 + }; 422 + 423 + static const struct property_entry whitelabel_tm800a550l_accel_props[] = { 424 + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), 425 + { } 426 + }; 427 + 428 + static const struct software_node whitelabel_tm800a550l_accel_node = { 429 + .properties = whitelabel_tm800a550l_accel_props, 430 + }; 431 + 432 + static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { 433 + PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), 434 + PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), 435 + PROPERTY_ENTRY_U32("goodix,main-clk", 54), 436 + { } 437 + }; 438 + 439 + static const struct software_node whitelabel_tm800a550l_goodix_node = { 440 + .properties = whitelabel_tm800a550l_goodix_props, 441 + }; 442 + 443 + static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { 444 + { 445 + /* goodix touchscreen */ 446 + .board_info = { 447 + .type = "GDIX1001:00", 448 + .addr = 0x14, 449 + .dev_name = "goodix_ts", 450 + .swnode = &whitelabel_tm800a550l_goodix_node, 451 + }, 452 + .adapter_path = "\\_SB_.I2C2", 453 + .irq_data = { 454 + .type = X86_ACPI_IRQ_TYPE_APIC, 455 + .index = 0x44, 456 + .trigger = ACPI_EDGE_SENSITIVE, 457 + .polarity = ACPI_ACTIVE_HIGH, 458 + }, 459 + }, { 460 + /* kxcj91008 accel */ 461 + .board_info = { 462 + .type = "kxcj91008", 463 + .addr = 0x0f, 464 + .dev_name = "kxcj91008", 465 + .swnode = &whitelabel_tm800a550l_accel_node, 466 + }, 467 + .adapter_path = "\\_SB_.I2C3", 468 + }, 469 + }; 470 + 471 + static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { 472 + .dev_id = "i2c-goodix_ts", 473 + .table = { 474 + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), 475 + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), 476 + { } 477 + }, 478 + }; 479 + 480 + static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { 481 + &whitelabel_tm800a550l_goodix_gpios, 482 + NULL 483 + }; 484 + 485 + const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { 486 + .i2c_client_info = whitelabel_tm800a550l_i2c_clients, 487 + .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), 488 + .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, 489 + }; 490 + 491 + /* 492 + * If the EFI bootloader is not Xiaomi's own signed Android loader, then the 493 + * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing 494 + * a bunch of devices to be hidden. 495 + * 496 + * This takes care of instantiating the hidden devices manually. 497 + */ 498 + static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { 499 + { 500 + /* BQ27520 fuel-gauge */ 501 + .board_info = { 502 + .type = "bq27520", 503 + .addr = 0x55, 504 + .dev_name = "bq27520", 505 + .swnode = &fg_bq25890_supply_node, 506 + }, 507 + .adapter_path = "\\_SB_.PCI0.I2C1", 508 + }, { 509 + /* KTD2026 RGB notification LED controller */ 510 + .board_info = { 511 + .type = "ktd2026", 512 + .addr = 0x30, 513 + .dev_name = "ktd2026", 514 + }, 515 + .adapter_path = "\\_SB_.PCI0.I2C3", 516 + }, 517 + }; 518 + 519 + const struct x86_dev_info xiaomi_mipad2_info __initconst = { 520 + .i2c_client_info = xiaomi_mipad2_i2c_clients, 521 + .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), 522 + };
+100
drivers/platform/x86/x86-android-tablets/shared-psy-info.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Shared psy info for X86 tablets which ship with Android as the factory image 4 + * and which have broken DSDT tables. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #include <linux/gpio/machine.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/power/bq24190_charger.h> 14 + #include <linux/property.h> 15 + #include <linux/regulator/machine.h> 16 + 17 + #include "shared-psy-info.h" 18 + 19 + /* Generic / shared charger / battery settings */ 20 + const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; 21 + const char * const bq24190_psy[] = { "bq24190-charger" }; 22 + const char * const bq25890_psy[] = { "bq25890-charger-0" }; 23 + 24 + static const struct property_entry fg_bq24190_supply_props[] = { 25 + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), 26 + { } 27 + }; 28 + 29 + const struct software_node fg_bq24190_supply_node = { 30 + .properties = fg_bq24190_supply_props, 31 + }; 32 + 33 + static const struct property_entry fg_bq25890_supply_props[] = { 34 + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy), 35 + { } 36 + }; 37 + 38 + const struct software_node fg_bq25890_supply_node = { 39 + .properties = fg_bq25890_supply_props, 40 + }; 41 + 42 + /* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ 43 + static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { 44 + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), 45 + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), 46 + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), 47 + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), 48 + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), 49 + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), 50 + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 51 + { } 52 + }; 53 + 54 + const struct software_node generic_lipo_hv_4v35_battery_node = { 55 + .properties = generic_lipo_hv_4v35_battery_props, 56 + }; 57 + 58 + /* For enabling the bq24190 5V boost based on id-pin */ 59 + static struct regulator_consumer_supply intel_int3496_consumer = { 60 + .supply = "vbus", 61 + .dev_name = "intel-int3496", 62 + }; 63 + 64 + static const struct regulator_init_data bq24190_vbus_init_data = { 65 + .constraints = { 66 + .name = "bq24190_vbus", 67 + .valid_ops_mask = REGULATOR_CHANGE_STATUS, 68 + }, 69 + .consumer_supplies = &intel_int3496_consumer, 70 + .num_consumer_supplies = 1, 71 + }; 72 + 73 + struct bq24190_platform_data bq24190_pdata = { 74 + .regulator_init_data = &bq24190_vbus_init_data, 75 + }; 76 + 77 + const char * const bq24190_modules[] __initconst = { 78 + "intel_crystal_cove_charger", /* For the bq24190 IRQ */ 79 + "bq24190_charger", /* For the Vbus regulator for intel-int3496 */ 80 + NULL 81 + }; 82 + 83 + /* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ 84 + const struct platform_device_info int3496_pdevs[] __initconst = { 85 + { 86 + /* For micro USB ID pin handling */ 87 + .name = "intel-int3496", 88 + .id = PLATFORM_DEVID_NONE, 89 + }, 90 + }; 91 + 92 + struct gpiod_lookup_table int3496_reference_gpios = { 93 + .dev_id = "intel-int3496", 94 + .table = { 95 + GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), 96 + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), 97 + GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), 98 + { } 99 + }, 100 + };
+32
drivers/platform/x86/x86-android-tablets/shared-psy-info.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later 2 + * 3 + * Shared psy info for X86 tablets which ship with Android as the factory image 4 + * and which have broken DSDT tables. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + #ifndef __PDX86_SHARED_PSY_INFO_H 11 + #define __PDX86_SHARED_PSY_INFO_H 12 + 13 + struct bq24190_platform_data; 14 + struct gpiod_lookup_table; 15 + struct platform_device_info; 16 + struct software_node; 17 + 18 + extern const char * const tusb1211_chg_det_psy[]; 19 + extern const char * const bq24190_psy[]; 20 + extern const char * const bq25890_psy[]; 21 + 22 + extern const struct software_node fg_bq24190_supply_node; 23 + extern const struct software_node fg_bq25890_supply_node; 24 + extern const struct software_node generic_lipo_hv_4v35_battery_node; 25 + 26 + extern struct bq24190_platform_data bq24190_pdata; 27 + extern const char * const bq24190_modules[]; 28 + 29 + extern const struct platform_device_info int3496_pdevs[]; 30 + extern struct gpiod_lookup_table int3496_reference_gpios; 31 + 32 + #endif
+108
drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later 2 + * 3 + * DMI based code to deal with broken DSDTs on X86 tablets which ship with 4 + * Android as (part of) the factory image. The factory kernels shipped on these 5 + * devices typically have a bunch of things hardcoded, rather than specified 6 + * in their DSDT. 7 + * 8 + * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + #ifndef __PDX86_X86_ANDROID_TABLETS_H 11 + #define __PDX86_X86_ANDROID_TABLETS_H 12 + 13 + #include <linux/gpio_keys.h> 14 + #include <linux/i2c.h> 15 + #include <linux/irqdomain_defs.h> 16 + 17 + struct gpio_desc; 18 + struct gpiod_lookup_table; 19 + struct platform_device_info; 20 + struct software_node; 21 + 22 + /* 23 + * Helpers to get Linux IRQ numbers given a description of the IRQ source 24 + * (either IOAPIC index, or GPIO chip name + pin-number). 25 + */ 26 + enum x86_acpi_irq_type { 27 + X86_ACPI_IRQ_TYPE_NONE, 28 + X86_ACPI_IRQ_TYPE_APIC, 29 + X86_ACPI_IRQ_TYPE_GPIOINT, 30 + X86_ACPI_IRQ_TYPE_PMIC, 31 + }; 32 + 33 + struct x86_acpi_irq_data { 34 + char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */ 35 + enum x86_acpi_irq_type type; 36 + enum irq_domain_bus_token domain; 37 + int index; 38 + int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */ 39 + int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */ 40 + }; 41 + 42 + /* Structs to describe devices to instantiate */ 43 + struct x86_i2c_client_info { 44 + struct i2c_board_info board_info; 45 + char *adapter_path; 46 + struct x86_acpi_irq_data irq_data; 47 + }; 48 + 49 + struct x86_serdev_info { 50 + const char *ctrl_hid; 51 + const char *ctrl_uid; 52 + const char *ctrl_devname; 53 + /* 54 + * ATM the serdev core only supports of or ACPI matching; and sofar all 55 + * Android x86 tablets DSDTs have usable serdev nodes, but sometimes 56 + * under the wrong controller. So we just tie the existing serdev ACPI 57 + * node to the right controller. 58 + */ 59 + const char *serdev_hid; 60 + }; 61 + 62 + struct x86_gpio_button { 63 + struct gpio_keys_button button; 64 + const char *chip; 65 + int pin; 66 + }; 67 + 68 + struct x86_dev_info { 69 + char *invalid_aei_gpiochip; 70 + const char * const *modules; 71 + const struct software_node *bat_swnode; 72 + struct gpiod_lookup_table * const *gpiod_lookup_tables; 73 + const struct x86_i2c_client_info *i2c_client_info; 74 + const struct platform_device_info *pdev_info; 75 + const struct x86_serdev_info *serdev_info; 76 + struct x86_gpio_button *gpio_button; 77 + int i2c_client_count; 78 + int pdev_count; 79 + int serdev_count; 80 + int (*init)(void); 81 + void (*exit)(void); 82 + }; 83 + 84 + int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc); 85 + int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data); 86 + 87 + /* 88 + * Extern declarations of x86_dev_info structs so there can be a single 89 + * MODULE_DEVICE_TABLE(dmi, ...), while splitting the board descriptions. 90 + */ 91 + extern const struct x86_dev_info acer_b1_750_info; 92 + extern const struct x86_dev_info advantech_mica_071_info; 93 + extern const struct x86_dev_info asus_me176c_info; 94 + extern const struct x86_dev_info asus_tf103c_info; 95 + extern const struct x86_dev_info chuwi_hi8_info; 96 + extern const struct x86_dev_info czc_p10t; 97 + extern const struct x86_dev_info lenovo_yogabook_x90_info; 98 + extern const struct x86_dev_info lenovo_yogabook_x91_info; 99 + extern const struct x86_dev_info lenovo_yoga_tab2_830_1050_info; 100 + extern const struct x86_dev_info lenovo_yt3_info; 101 + extern const struct x86_dev_info medion_lifetab_s10346_info; 102 + extern const struct x86_dev_info nextbook_ares8_info; 103 + extern const struct x86_dev_info peaq_c1010_info; 104 + extern const struct x86_dev_info whitelabel_tm800a550l_info; 105 + extern const struct x86_dev_info xiaomi_mipad2_info; 106 + extern const struct dmi_system_id x86_android_tablet_ids[]; 107 + 108 + #endif
+2 -3
drivers/platform/x86/xo1-rfkill.c
··· 56 56 return 0; 57 57 } 58 58 59 - static int xo1_rfkill_remove(struct platform_device *pdev) 59 + static void xo1_rfkill_remove(struct platform_device *pdev) 60 60 { 61 61 struct rfkill *rfk = platform_get_drvdata(pdev); 62 62 rfkill_unregister(rfk); 63 63 rfkill_destroy(rfk); 64 - return 0; 65 64 } 66 65 67 66 static struct platform_driver xo1_rfkill_driver = { ··· 68 69 .name = "xo1-rfkill", 69 70 }, 70 71 .probe = xo1_rfkill_probe, 71 - .remove = xo1_rfkill_remove, 72 + .remove_new = xo1_rfkill_remove, 72 73 }; 73 74 74 75 module_platform_driver(xo1_rfkill_driver);
+1
drivers/video/backlight/Kconfig
··· 285 285 config BACKLIGHT_APPLE 286 286 tristate "Apple Backlight Driver" 287 287 depends on X86 && ACPI 288 + depends on ACPI_VIDEO=n || ACPI_VIDEO 288 289 help 289 290 If you have an Intel-based Apple say Y to enable a driver for its 290 291 backlight.
+10 -21
drivers/video/backlight/apple_bl.c
··· 24 24 #include <linux/pci.h> 25 25 #include <linux/acpi.h> 26 26 #include <linux/atomic.h> 27 - #include <linux/apple_bl.h> 27 + #include <acpi/video.h> 28 28 29 29 static struct backlight_device *apple_backlight_device; 30 30 ··· 215 215 }, 216 216 }; 217 217 218 - static atomic_t apple_bl_registered = ATOMIC_INIT(0); 219 - 220 - int apple_bl_register(void) 221 - { 222 - if (atomic_xchg(&apple_bl_registered, 1) == 0) 223 - return acpi_bus_register_driver(&apple_bl_driver); 224 - 225 - return 0; 226 - } 227 - EXPORT_SYMBOL_GPL(apple_bl_register); 228 - 229 - void apple_bl_unregister(void) 230 - { 231 - if (atomic_xchg(&apple_bl_registered, 0) == 1) 232 - acpi_bus_unregister_driver(&apple_bl_driver); 233 - } 234 - EXPORT_SYMBOL_GPL(apple_bl_unregister); 235 - 236 218 static int __init apple_bl_init(void) 237 219 { 238 - return apple_bl_register(); 220 + /* 221 + * Use ACPI video detection code to see if this driver should register 222 + * or if another driver, e.g. the apple-gmux driver should be used. 223 + */ 224 + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 225 + return -ENODEV; 226 + 227 + return acpi_bus_register_driver(&apple_bl_driver); 239 228 } 240 229 241 230 static void __exit apple_bl_exit(void) 242 231 { 243 - apple_bl_unregister(); 232 + acpi_bus_unregister_driver(&apple_bl_driver); 244 233 } 245 234 246 235 module_init(apple_bl_init);
+54 -18
include/linux/apple-gmux.h
··· 34 34 #define GMUX_PORT_READ 0xd0 35 35 #define GMUX_PORT_WRITE 0xd4 36 36 37 + #define GMUX_MMIO_PORT_SELECT 0x0e 38 + #define GMUX_MMIO_COMMAND_SEND 0x0f 39 + 40 + #define GMUX_MMIO_READ 0x00 41 + #define GMUX_MMIO_WRITE 0x40 42 + 37 43 #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) 44 + 45 + enum apple_gmux_type { 46 + APPLE_GMUX_TYPE_PIO, 47 + APPLE_GMUX_TYPE_INDEXED, 48 + APPLE_GMUX_TYPE_MMIO, 49 + }; 38 50 39 51 #if IS_ENABLED(CONFIG_APPLE_GMUX) 40 52 static inline bool apple_gmux_is_indexed(unsigned long iostart) ··· 64 52 return false; 65 53 } 66 54 55 + static inline bool apple_gmux_is_mmio(unsigned long iostart) 56 + { 57 + u8 __iomem *iomem_base = ioremap(iostart, 16); 58 + u8 val; 59 + 60 + if (!iomem_base) 61 + return false; 62 + 63 + /* 64 + * If this is 0xff, then gmux must not be present, as the gmux would 65 + * reset it to 0x00, or it would be one of 0x1, 0x4, 0x41, 0x44 if a 66 + * command is currently being processed. 67 + */ 68 + val = ioread8(iomem_base + GMUX_MMIO_COMMAND_SEND); 69 + iounmap(iomem_base); 70 + return (val != 0xff); 71 + } 72 + 67 73 /** 68 74 * apple_gmux_detect() - detect if gmux is built into the machine 69 75 * 70 76 * @pnp_dev: Device to probe or NULL to use the first matching device 71 - * @indexed_ret: Returns (by reference) if the gmux is indexed or not 77 + * @type_ret: Returns (by reference) the apple_gmux_type of the device 72 78 * 73 79 * Detect if a supported gmux device is present by actually probing it. 74 80 * This avoids the false positives returned on some models by ··· 95 65 * Return: %true if a supported gmux ACPI device is detected and the kernel 96 66 * was configured with CONFIG_APPLE_GMUX, %false otherwise. 97 67 */ 98 - static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret) 68 + static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_type *type_ret) 99 69 { 100 70 u8 ver_major, ver_minor, ver_release; 101 71 struct device *dev = NULL; 102 72 struct acpi_device *adev; 103 73 struct resource *res; 104 - bool indexed = false; 74 + enum apple_gmux_type type = APPLE_GMUX_TYPE_PIO; 105 75 bool ret = false; 106 76 107 77 if (!pnp_dev) { ··· 118 88 } 119 89 120 90 res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0); 121 - if (!res || resource_size(res) < GMUX_MIN_IO_LEN) 122 - goto out; 123 - 124 - /* 125 - * Invalid version information may indicate either that the gmux 126 - * device isn't present or that it's a new one that uses indexed io. 127 - */ 128 - ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR); 129 - ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR); 130 - ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE); 131 - if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { 132 - indexed = apple_gmux_is_indexed(res->start); 133 - if (!indexed) 91 + if (res && resource_size(res) >= GMUX_MIN_IO_LEN) { 92 + /* 93 + * Invalid version information may indicate either that the gmux 94 + * device isn't present or that it's a new one that uses indexed io. 95 + */ 96 + ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR); 97 + ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR); 98 + ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE); 99 + if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { 100 + if (apple_gmux_is_indexed(res->start)) 101 + type = APPLE_GMUX_TYPE_INDEXED; 102 + else 103 + goto out; 104 + } 105 + } else { 106 + res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0); 107 + if (res && apple_gmux_is_mmio(res->start)) 108 + type = APPLE_GMUX_TYPE_MMIO; 109 + else 134 110 goto out; 135 111 } 136 112 137 - if (indexed_ret) 138 - *indexed_ret = indexed; 113 + if (type_ret) 114 + *type_ret = type; 139 115 140 116 ret = true; 141 117 out:
-27
include/linux/apple_bl.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0 */ 2 - /* 3 - * apple_bl exported symbols 4 - */ 5 - 6 - #ifndef _LINUX_APPLE_BL_H 7 - #define _LINUX_APPLE_BL_H 8 - 9 - #if defined(CONFIG_BACKLIGHT_APPLE) || defined(CONFIG_BACKLIGHT_APPLE_MODULE) 10 - 11 - extern int apple_bl_register(void); 12 - extern void apple_bl_unregister(void); 13 - 14 - #else /* !CONFIG_BACKLIGHT_APPLE */ 15 - 16 - static inline int apple_bl_register(void) 17 - { 18 - return 0; 19 - } 20 - 21 - static inline void apple_bl_unregister(void) 22 - { 23 - } 24 - 25 - #endif /* !CONFIG_BACKLIGHT_APPLE */ 26 - 27 - #endif /* _LINUX_APPLE_BL_H */
+303
include/uapi/linux/isst_if.h
··· 163 163 struct isst_if_msr_cmd msr_cmd[1]; 164 164 }; 165 165 166 + /** 167 + * struct isst_core_power - Structure to get/set core_power feature 168 + * @get_set: 0: Get, 1: Set 169 + * @socket_id: Socket/package id 170 + * @power_domain: Power Domain id 171 + * @enable: Feature enable status 172 + * @priority_type: Priority type for the feature (ordered/proportional) 173 + * 174 + * Structure to get/set core_power feature state using IOCTL 175 + * ISST_IF_CORE_POWER_STATE. 176 + */ 177 + struct isst_core_power { 178 + __u8 get_set; 179 + __u8 socket_id; 180 + __u8 power_domain_id; 181 + __u8 enable; 182 + __u8 supported; 183 + __u8 priority_type; 184 + }; 185 + 186 + /** 187 + * struct isst_clos_param - Structure to get/set clos praram 188 + * @get_set: 0: Get, 1: Set 189 + * @socket_id: Socket/package id 190 + * @power_domain: Power Domain id 191 + * clos: Clos ID for the parameters 192 + * min_freq_mhz: Minimum frequency in MHz 193 + * max_freq_mhz: Maximum frequency in MHz 194 + * prop_prio: Proportional priority from 0-15 195 + * 196 + * Structure to get/set per clos property using IOCTL 197 + * ISST_IF_CLOS_PARAM. 198 + */ 199 + struct isst_clos_param { 200 + __u8 get_set; 201 + __u8 socket_id; 202 + __u8 power_domain_id; 203 + __u8 clos; 204 + __u16 min_freq_mhz; 205 + __u16 max_freq_mhz; 206 + __u8 prop_prio; 207 + }; 208 + 209 + /** 210 + * struct isst_if_clos_assoc - Structure to assign clos to a CPU 211 + * @socket_id: Socket/package id 212 + * @power_domain: Power Domain id 213 + * @logical_cpu: CPU number 214 + * @clos: Clos ID to assign to the logical CPU 215 + * 216 + * Structure to get/set core_power feature. 217 + */ 218 + struct isst_if_clos_assoc { 219 + __u8 socket_id; 220 + __u8 power_domain_id; 221 + __u16 logical_cpu; 222 + __u16 clos; 223 + }; 224 + 225 + /** 226 + * struct isst_if_clos_assoc_cmds - Structure to assign clos to CPUs 227 + * @cmd_count: Number of cmds (cpus) in this request 228 + * @get_set: Request is for get or set 229 + * @punit_cpu_map: Set to 1 if the CPU number is punit numbering not 230 + * Linux CPU number 231 + * 232 + * Structure used to get/set associate CPUs to clos using IOCTL 233 + * ISST_IF_CLOS_ASSOC. 234 + */ 235 + struct isst_if_clos_assoc_cmds { 236 + __u16 cmd_count; 237 + __u16 get_set; 238 + __u16 punit_cpu_map; 239 + struct isst_if_clos_assoc assoc_info[1]; 240 + }; 241 + 242 + /** 243 + * struct isst_tpmi_instance_count - Get number of TPMI instances per socket 244 + * @socket_id: Socket/package id 245 + * @count: Number of instances 246 + * @valid_mask: Mask of instances as there can be holes 247 + * 248 + * Structure used to get TPMI instances information using 249 + * IOCTL ISST_IF_COUNT_TPMI_INSTANCES. 250 + */ 251 + struct isst_tpmi_instance_count { 252 + __u8 socket_id; 253 + __u8 count; 254 + __u16 valid_mask; 255 + }; 256 + 257 + /** 258 + * struct isst_perf_level_info - Structure to get information on SST-PP levels 259 + * @socket_id: Socket/package id 260 + * @power_domain: Power Domain id 261 + * @logical_cpu: CPU number 262 + * @clos: Clos ID to assign to the logical CPU 263 + * @max_level: Maximum performance level supported by the platform 264 + * @feature_rev: The feature revision for SST-PP supported by the platform 265 + * @level_mask: Mask of supported performance levels 266 + * @current_level: Current performance level 267 + * @feature_state: SST-BF and SST-TF (enabled/disabled) status at current level 268 + * @locked: SST-PP performance level change is locked/unlocked 269 + * @enabled: SST-PP feature is enabled or not 270 + * @sst-tf_support: SST-TF support status at this level 271 + * @sst-bf_support: SST-BF support status at this level 272 + * 273 + * Structure to get SST-PP details using IOCTL ISST_IF_PERF_LEVELS. 274 + */ 275 + struct isst_perf_level_info { 276 + __u8 socket_id; 277 + __u8 power_domain_id; 278 + __u8 max_level; 279 + __u8 feature_rev; 280 + __u8 level_mask; 281 + __u8 current_level; 282 + __u8 feature_state; 283 + __u8 locked; 284 + __u8 enabled; 285 + __u8 sst_tf_support; 286 + __u8 sst_bf_support; 287 + }; 288 + 289 + /** 290 + * struct isst_perf_level_control - Structure to set SST-PP level 291 + * @socket_id: Socket/package id 292 + * @power_domain: Power Domain id 293 + * @level: level to set 294 + * 295 + * Structure used change SST-PP level using IOCTL ISST_IF_PERF_SET_LEVEL. 296 + */ 297 + struct isst_perf_level_control { 298 + __u8 socket_id; 299 + __u8 power_domain_id; 300 + __u8 level; 301 + }; 302 + 303 + /** 304 + * struct isst_perf_feature_control - Structure to activate SST-BF/SST-TF 305 + * @socket_id: Socket/package id 306 + * @power_domain: Power Domain id 307 + * @feature: bit 0 = SST-BF state, bit 1 = SST-TF state 308 + * 309 + * Structure used to enable SST-BF/SST-TF using IOCTL ISST_IF_PERF_SET_FEATURE. 310 + */ 311 + struct isst_perf_feature_control { 312 + __u8 socket_id; 313 + __u8 power_domain_id; 314 + __u8 feature; 315 + }; 316 + 317 + #define TRL_MAX_BUCKETS 8 318 + #define TRL_MAX_LEVELS 6 319 + 320 + /** 321 + * struct isst_perf_level_data_info - Structure to get SST-PP level details 322 + * @socket_id: Socket/package id 323 + * @power_domain: Power Domain id 324 + * @level: SST-PP level for which caller wants to get information 325 + * @tdp_ratio: TDP Ratio 326 + * @base_freq_mhz: Base frequency in MHz 327 + * @base_freq_avx2_mhz: AVX2 Base frequency in MHz 328 + * @base_freq_avx512_mhz: AVX512 base frequency in MHz 329 + * @base_freq_amx_mhz: AMX base frequency in MHz 330 + * @thermal_design_power_w: Thermal design (TDP) power 331 + * @tjunction_max_c: Max junction temperature 332 + * @max_memory_freq_mhz: Max memory frequency in MHz 333 + * @cooling_type: Type of cooling is used 334 + * @p0_freq_mhz: core maximum frequency 335 + * @p1_freq_mhz: Core TDP frequency 336 + * @pn_freq_mhz: Core maximum efficiency frequency 337 + * @pm_freq_mhz: Core minimum frequency 338 + * @p0_fabric_freq_mhz: Fabric (Uncore) maximum frequency 339 + * @p1_fabric_freq_mhz: Fabric (Uncore) TDP frequency 340 + * @pn_fabric_freq_mhz: Fabric (Uncore) minimum efficiency frequency 341 + * @pm_fabric_freq_mhz: Fabric (Uncore) minimum frequency 342 + * @max_buckets: Maximum trl buckets 343 + * @max_trl_levels: Maximum trl levels 344 + * @bucket_core_counts[TRL_MAX_BUCKETS]: Number of cores per bucket 345 + * @trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]: maximum frequency 346 + * for a bucket and trl level 347 + * 348 + * Structure used to get information on frequencies and TDP for a SST-PP 349 + * level using ISST_IF_GET_PERF_LEVEL_INFO. 350 + */ 351 + struct isst_perf_level_data_info { 352 + __u8 socket_id; 353 + __u8 power_domain_id; 354 + __u16 level; 355 + __u16 tdp_ratio; 356 + __u16 base_freq_mhz; 357 + __u16 base_freq_avx2_mhz; 358 + __u16 base_freq_avx512_mhz; 359 + __u16 base_freq_amx_mhz; 360 + __u16 thermal_design_power_w; 361 + __u16 tjunction_max_c; 362 + __u16 max_memory_freq_mhz; 363 + __u16 cooling_type; 364 + __u16 p0_freq_mhz; 365 + __u16 p1_freq_mhz; 366 + __u16 pn_freq_mhz; 367 + __u16 pm_freq_mhz; 368 + __u16 p0_fabric_freq_mhz; 369 + __u16 p1_fabric_freq_mhz; 370 + __u16 pn_fabric_freq_mhz; 371 + __u16 pm_fabric_freq_mhz; 372 + __u16 max_buckets; 373 + __u16 max_trl_levels; 374 + __u16 bucket_core_counts[TRL_MAX_BUCKETS]; 375 + __u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]; 376 + }; 377 + 378 + /** 379 + * struct isst_perf_level_cpu_mask - Structure to get SST-PP level CPU mask 380 + * @socket_id: Socket/package id 381 + * @power_domain: Power Domain id 382 + * @level: SST-PP level for which caller wants to get information 383 + * @punit_cpu_map: Set to 1 if the CPU number is punit numbering not 384 + * Linux CPU number. If 0 CPU buffer is copied to user space 385 + * supplied cpu_buffer of size cpu_buffer_size. Punit 386 + * cpu mask is copied to "mask" field. 387 + * @mask: cpu mask for this PP level (punit CPU numbering) 388 + * @cpu_buffer_size: size of cpu_buffer also used to return the copied CPU 389 + * buffer size. 390 + * @cpu_buffer: Buffer to copy CPU mask when punit_cpu_map is 0 391 + * 392 + * Structure used to get cpumask for a SST-PP level using 393 + * IOCTL ISST_IF_GET_PERF_LEVEL_CPU_MASK. Also used to get CPU mask for 394 + * IOCTL ISST_IF_GET_BASE_FREQ_CPU_MASK for SST-BF. 395 + */ 396 + struct isst_perf_level_cpu_mask { 397 + __u8 socket_id; 398 + __u8 power_domain_id; 399 + __u8 level; 400 + __u8 punit_cpu_map; 401 + __u64 mask; 402 + __u16 cpu_buffer_size; 403 + __s8 cpu_buffer[1]; 404 + }; 405 + 406 + /** 407 + * struct isst_base_freq_info - Structure to get SST-BF frequencies 408 + * @socket_id: Socket/package id 409 + * @power_domain: Power Domain id 410 + * @level: SST-PP level for which caller wants to get information 411 + * @high_base_freq_mhz: High priority CPU base frequency 412 + * @low_base_freq_mhz: Low priority CPU base frequency 413 + * @tjunction_max_c: Max junction temperature 414 + * @thermal_design_power_w: Thermal design power in watts 415 + * 416 + * Structure used to get SST-BF information using 417 + * IOCTL ISST_IF_GET_BASE_FREQ_INFO. 418 + */ 419 + struct isst_base_freq_info { 420 + __u8 socket_id; 421 + __u8 power_domain_id; 422 + __u16 level; 423 + __u16 high_base_freq_mhz; 424 + __u16 low_base_freq_mhz; 425 + __u16 tjunction_max_c; 426 + __u16 thermal_design_power_w; 427 + }; 428 + 429 + /** 430 + * struct isst_turbo_freq_info - Structure to get SST-TF frequencies 431 + * @socket_id: Socket/package id 432 + * @power_domain: Power Domain id 433 + * @level: SST-PP level for which caller wants to get information 434 + * @max_clip_freqs: Maximum number of low priority core clipping frequencies 435 + * @lp_clip_freq_mhz: Clip frequencies per trl level 436 + * @bucket_core_counts: Maximum number of cores for a bucket 437 + * @trl_freq_mhz: Frequencies per trl level for each bucket 438 + * 439 + * Structure used to get SST-TF information using 440 + * IOCTL ISST_IF_GET_TURBO_FREQ_INFO. 441 + */ 442 + struct isst_turbo_freq_info { 443 + __u8 socket_id; 444 + __u8 power_domain_id; 445 + __u16 level; 446 + __u16 max_clip_freqs; 447 + __u16 max_buckets; 448 + __u16 max_trl_levels; 449 + __u16 lp_clip_freq_mhz[TRL_MAX_LEVELS]; 450 + __u16 bucket_core_counts[TRL_MAX_BUCKETS]; 451 + __u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]; 452 + }; 453 + 166 454 #define ISST_IF_MAGIC 0xFE 167 455 #define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *) 168 456 #define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *) 169 457 #define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *) 170 458 #define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *) 171 459 #define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *) 460 + 461 + #define ISST_IF_COUNT_TPMI_INSTANCES _IOR(ISST_IF_MAGIC, 5, struct isst_tpmi_instance_count *) 462 + #define ISST_IF_CORE_POWER_STATE _IOWR(ISST_IF_MAGIC, 6, struct isst_core_power *) 463 + #define ISST_IF_CLOS_PARAM _IOWR(ISST_IF_MAGIC, 7, struct isst_clos_param *) 464 + #define ISST_IF_CLOS_ASSOC _IOWR(ISST_IF_MAGIC, 8, struct isst_if_clos_assoc_cmds *) 465 + 466 + #define ISST_IF_PERF_LEVELS _IOWR(ISST_IF_MAGIC, 9, struct isst_perf_level_info *) 467 + #define ISST_IF_PERF_SET_LEVEL _IOW(ISST_IF_MAGIC, 10, struct isst_perf_level_control *) 468 + #define ISST_IF_PERF_SET_FEATURE _IOW(ISST_IF_MAGIC, 11, struct isst_perf_feature_control *) 469 + #define ISST_IF_GET_PERF_LEVEL_INFO _IOR(ISST_IF_MAGIC, 12, struct isst_perf_level_data_info *) 470 + #define ISST_IF_GET_PERF_LEVEL_CPU_MASK _IOR(ISST_IF_MAGIC, 13, struct isst_perf_level_cpu_mask *) 471 + #define ISST_IF_GET_BASE_FREQ_INFO _IOR(ISST_IF_MAGIC, 14, struct isst_base_freq_info *) 472 + #define ISST_IF_GET_BASE_FREQ_CPU_MASK _IOR(ISST_IF_MAGIC, 15, struct isst_perf_level_cpu_mask *) 473 + #define ISST_IF_GET_TURBO_FREQ_INFO _IOR(ISST_IF_MAGIC, 16, struct isst_turbo_freq_info *) 474 + 172 475 #endif
+1 -1
tools/power/x86/intel-speed-select/Build
··· 1 - intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o 1 + 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
+437 -364
tools/power/x86/intel-speed-select/isst-config.c
··· 15 15 int arg; 16 16 }; 17 17 18 - static const char *version_str = "v1.14"; 18 + static const char *version_str = "v1.15"; 19 19 20 - static const int supported_api_ver = 1; 20 + static const int supported_api_ver = 2; 21 21 static struct isst_if_platform_info isst_platform_info; 22 22 static char *progname; 23 23 static int debug_flag; ··· 44 44 static int force_online_offline; 45 45 static int auto_mode; 46 46 static int fact_enable_fail; 47 - 48 - static int mbox_delay; 49 - static int mbox_retries = 3; 47 + static int cgroupv2; 50 48 51 49 /* clos related */ 52 50 static int current_clos = -1; ··· 59 61 unsigned short core_id; 60 62 unsigned short pkg_id; 61 63 unsigned short die_id; 64 + unsigned short punit_id; 62 65 unsigned short punit_cpu; 63 66 unsigned short punit_cpu_core; 64 67 unsigned short initialized; ··· 76 77 FILE *get_output_file(void) 77 78 { 78 79 return outf; 80 + } 81 + 82 + int is_debug_enabled(void) 83 + { 84 + return debug_flag; 79 85 } 80 86 81 87 void debug_printf(const char *format, ...) ··· 114 110 115 111 int is_spr_platform(void) 116 112 { 117 - if (cpu_model == 0x8F || cpu_model == 0xCF) 113 + if (cpu_model == 0x8F) 118 114 return 1; 119 115 120 116 return 0; 121 117 } 118 + 119 + int is_emr_platform(void) 120 + { 121 + if (cpu_model == 0xCF) 122 + return 1; 123 + 124 + return 0; 125 + } 126 + 122 127 123 128 int is_icx_platform(void) 124 129 { ··· 174 161 return ret; 175 162 } 176 163 return 0; 164 + } 165 + 166 + int api_version(void) 167 + { 168 + return isst_platform_info.api_version; 177 169 } 178 170 179 171 /* Open a file, and exit on failure */ ··· 396 378 return ret; 397 379 } 398 380 381 + static int get_physical_punit_id(int cpu) 382 + { 383 + if (cpu < 0) 384 + return -1; 385 + 386 + if (cpu_map && cpu_map[cpu].initialized) 387 + return cpu_map[cpu].punit_id; 388 + 389 + return -1; 390 + } 391 + 399 392 void set_isst_id(struct isst_id *id, int cpu) 400 393 { 401 394 id->cpu = cpu; ··· 418 389 id->die = get_physical_die_id(cpu); 419 390 if (id->die >= MAX_DIE_PER_PACKAGE) 420 391 id->die = -1; 392 + 393 + id->punit = get_physical_punit_id(cpu); 394 + if (id->punit >= MAX_PUNIT_PER_DIE) 395 + id->punit = -1; 421 396 } 422 397 423 398 int is_cpu_in_power_domain(int cpu, struct isst_id *id) ··· 430 397 431 398 set_isst_id(&tid, cpu); 432 399 433 - if (id->pkg == tid.pkg && id->die == tid.die) 400 + if (id->pkg == tid.pkg && id->die == tid.die && id->punit == tid.punit) 434 401 return 1; 435 402 436 403 return 0; ··· 514 481 unlink("/var/run/isst_cpu_topology.dat"); 515 482 } 516 483 517 - void for_each_online_package_in_set(void (*callback)(struct isst_id *, void *, void *, 484 + void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *, 518 485 void *, void *), 519 486 void *arg1, void *arg2, void *arg3, 520 487 void *arg4) 521 488 { 522 - int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT]; 523 - int pkg_index = 0, i; 524 489 struct isst_id id; 490 + int cpus[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; 491 + int valid_mask[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE] = {0}; 492 + int i, j, k; 525 493 526 - memset(max_packages, 0xff, sizeof(max_packages)); 494 + memset(cpus, -1, sizeof(cpus)); 495 + 527 496 for (i = 0; i < topo_max_cpus; ++i) { 528 - int j, online, pkg_id, die_id = 0, skip = 0; 497 + int online; 529 498 530 499 if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) 531 500 continue; 532 - if (i) 533 - online = parse_int_file( 534 - 1, "/sys/devices/system/cpu/cpu%d/online", i); 535 - else 536 - online = 537 - 1; /* online entry for CPU 0 needs some special configs */ 538 501 539 - die_id = get_physical_die_id(i); 540 - if (die_id < 0) 541 - die_id = 0; 502 + online = parse_int_file( 503 + i != 0, "/sys/devices/system/cpu/cpu%d/online", i); 504 + if (online < 0) 505 + online = 1; /* online entry for CPU 0 needs some special configs */ 542 506 543 - pkg_id = parse_int_file(0, 544 - "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i); 545 - if (pkg_id < 0) 507 + if (!online) 546 508 continue; 547 509 548 - /* Create an unique id for package, die combination to store */ 549 - pkg_id = (MAX_PACKAGE_COUNT * pkg_id + die_id); 550 - 551 - for (j = 0; j < pkg_index; ++j) { 552 - if (max_packages[j] == pkg_id) { 553 - skip = 1; 554 - break; 555 - } 556 - } 557 - 558 510 set_isst_id(&id, i); 559 - if (!skip && online && callback) { 560 - callback(&id, arg1, arg2, arg3, arg4); 561 - max_packages[pkg_index++] = pkg_id; 511 + 512 + if (id.pkg < 0 || id.die < 0 || id.punit < 0) 513 + continue; 514 + 515 + valid_mask[id.pkg][id.die] = 1; 516 + 517 + if (cpus[id.pkg][id.die][id.punit] == -1) 518 + cpus[id.pkg][id.die][id.punit] = i; 519 + } 520 + 521 + for (i = 0; i < MAX_PACKAGE_COUNT; i++) { 522 + for (j = 0; j < MAX_DIE_PER_PACKAGE; j++) { 523 + /* 524 + * Fix me: 525 + * How to check a non-cpu die for a package/die with all cpu offlined? 526 + */ 527 + if (!valid_mask[i][j]) 528 + continue; 529 + for (k = 0; k < MAX_PUNIT_PER_DIE; k++) { 530 + id.cpu = cpus[i][j][k]; 531 + id.pkg = i; 532 + id.die = j; 533 + id.punit = k; 534 + if (isst_is_punit_valid(&id)) 535 + callback(&id, arg1, arg2, arg3, arg4); 536 + } 562 537 } 563 538 } 564 539 } ··· 651 610 CPU_FREE(cpu_set); 652 611 } 653 612 654 - static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; 613 + static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; 655 614 656 615 int get_max_punit_core_id(struct isst_id *id) 657 616 { ··· 673 632 674 633 int get_cpu_count(struct isst_id *id) 675 634 { 676 - if (id->pkg < 0 || id->die < 0) 635 + if (id->pkg < 0 || id->die < 0 || id->punit < 0) 677 636 return 0; 678 637 679 - return cpu_cnt[id->pkg][id->die]; 638 + return cpu_cnt[id->pkg][id->die][id->punit]; 639 + } 640 + 641 + static void update_punit_cpu_info(__u32 physical_cpu, struct _cpu_map *cpu_map) 642 + { 643 + if (api_version() > 1) { 644 + /* 645 + * MSR 0x54 format 646 + * [15:11] PM_DOMAIN_ID 647 + * [10:3] MODULE_ID (aka IDI_AGENT_ID) 648 + * [2:0] LP_ID (We don't care about these bits we only 649 + * care die and core id 650 + * For Atom: 651 + * [2] Always 0 652 + * [1:0] core ID within module 653 + * For Core 654 + * [2:1] Always 0 655 + * [0] thread ID 656 + */ 657 + cpu_map->punit_id = (physical_cpu >> 11) & 0x1f; 658 + cpu_map->punit_cpu_core = (physical_cpu >> 3) & 0xff; 659 + cpu_map->punit_cpu = physical_cpu & 0x7ff; 660 + } else { 661 + int punit_id; 662 + 663 + /* 664 + * MSR 0x53 format 665 + * Format 666 + * Bit 0 – thread ID 667 + * Bit 8:1 – core ID 668 + * Bit 13:9 – punit ID 669 + */ 670 + cpu_map->punit_cpu = physical_cpu & 0x1ff; 671 + cpu_map->punit_cpu_core = (cpu_map->punit_cpu >> 1); // shift to get core id 672 + punit_id = (physical_cpu >> 9) & 0x1f; 673 + 674 + if (punit_id >= MAX_PUNIT_PER_DIE) 675 + punit_id = 0; 676 + 677 + cpu_map->punit_id = punit_id; 678 + } 680 679 } 681 680 682 681 static void create_cpu_map(void) ··· 741 660 742 661 for (i = 0; i < topo_max_cpus; ++i) { 743 662 char buffer[256]; 744 - int pkg_id, die_id, core_id; 663 + int pkg_id, die_id, core_id, punit_id; 745 664 746 665 /* check if CPU is online */ 747 666 snprintf(buffer, sizeof(buffer), ··· 763 682 cpu_map[i].pkg_id = pkg_id; 764 683 cpu_map[i].die_id = die_id; 765 684 cpu_map[i].core_id = core_id; 685 + 686 + 687 + punit_id = 0; 688 + 689 + if (fd >= 0) { 690 + map.cmd_count = 1; 691 + map.cpu_map[0].logical_cpu = i; 692 + debug_printf(" map logical_cpu:%d\n", 693 + map.cpu_map[0].logical_cpu); 694 + if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) { 695 + perror("ISST_IF_GET_PHY_ID"); 696 + fprintf(outf, "Error: map logical_cpu:%d\n", 697 + map.cpu_map[0].logical_cpu); 698 + } else { 699 + update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]); 700 + } 701 + } 766 702 cpu_map[i].initialized = 1; 767 703 768 - cpu_cnt[pkg_id][die_id]++; 769 - 770 - if (fd < 0) 771 - continue; 772 - map.cmd_count = 1; 773 - map.cpu_map[0].logical_cpu = i; 774 - debug_printf(" map logical_cpu:%d\n", 775 - map.cpu_map[0].logical_cpu); 776 - if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) { 777 - perror("ISST_IF_GET_PHY_ID"); 778 - fprintf(outf, "Error: map logical_cpu:%d\n", 779 - map.cpu_map[0].logical_cpu); 780 - continue; 781 - } 782 - cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu; 783 - cpu_map[i].punit_cpu_core = (map.cpu_map[0].physical_cpu >> 784 - 1); // shift to get core id 704 + cpu_cnt[pkg_id][die_id][punit_id]++; 785 705 786 706 debug_printf( 787 - "map logical_cpu:%d core: %d die:%d pkg:%d punit_cpu:%d punit_core:%d\n", 707 + "map logical_cpu:%d core: %d die:%d pkg:%d punit:%d punit_cpu:%d punit_core:%d\n", 788 708 i, cpu_map[i].core_id, cpu_map[i].die_id, 789 - cpu_map[i].pkg_id, cpu_map[i].punit_cpu, 790 - cpu_map[i].punit_cpu_core); 709 + cpu_map[i].pkg_id, cpu_map[i].punit_id, 710 + cpu_map[i].punit_cpu, cpu_map[i].punit_cpu_core); 791 711 } 792 712 if (fd >= 0) 793 713 close(fd); ··· 809 727 cpu_set_t *core_cpumask, int *cpu_cnt) 810 728 { 811 729 int i, cnt = 0; 730 + 731 + if (id->cpu < 0) 732 + return; 812 733 813 734 *cpu_cnt = 0; 814 735 ··· 844 759 return -EINVAL; 845 760 } 846 761 847 - static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write, 848 - unsigned int *value) 762 + int use_cgroupv2(void) 849 763 { 850 - struct isst_if_io_regs io_regs; 851 - const char *pathname = "/dev/isst_interface"; 852 - int cmd; 853 - int fd; 854 - 855 - debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); 856 - 857 - fd = open(pathname, O_RDWR); 858 - if (fd < 0) 859 - err(-1, "%s open failed", pathname); 860 - 861 - io_regs.req_count = 1; 862 - io_regs.io_reg[0].logical_cpu = cpu; 863 - io_regs.io_reg[0].reg = reg; 864 - cmd = ISST_IF_IO_CMD; 865 - if (write) { 866 - io_regs.io_reg[0].read_write = 1; 867 - io_regs.io_reg[0].value = *value; 868 - } else { 869 - io_regs.io_reg[0].read_write = 0; 870 - } 871 - 872 - if (ioctl(fd, cmd, &io_regs) == -1) { 873 - if (errno == ENOTTY) { 874 - perror("ISST_IF_IO_COMMAND\n"); 875 - fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); 876 - exit(0); 877 - } 878 - fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", 879 - cpu, reg, write); 880 - } else { 881 - if (!write) 882 - *value = io_regs.io_reg[0].value; 883 - 884 - debug_printf( 885 - "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", 886 - cpu, reg, write, *value); 887 - } 888 - 889 - close(fd); 890 - 891 - return 0; 764 + return cgroupv2; 892 765 } 893 766 894 - int isst_send_mbox_command(unsigned int cpu, unsigned char command, 895 - unsigned char sub_command, unsigned int parameter, 896 - unsigned int req_data, unsigned int *resp) 767 + int enable_cpuset_controller(void) 897 768 { 898 - const char *pathname = "/dev/isst_interface"; 899 - int fd, retry; 900 - struct isst_if_mbox_cmds mbox_cmds = { 0 }; 769 + int fd, ret; 901 770 902 - debug_printf( 903 - "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", 904 - cpu, command, sub_command, parameter, req_data); 771 + fd = open("/sys/fs/cgroup/cgroup.subtree_control", O_RDWR, 0); 772 + if (fd < 0) { 773 + debug_printf("Can't activate cpuset controller\n"); 774 + debug_printf("Either you are not root user or CGroup v2 is not supported\n"); 775 + return fd; 776 + } 905 777 906 - if (!is_skx_based_platform() && command == CONFIG_CLOS && 907 - sub_command != CLOS_PM_QOS_CONFIG) { 908 - unsigned int value; 909 - int write = 0; 910 - int clos_id, core_id, ret = 0; 778 + ret = write(fd, " +cpuset", strlen(" +cpuset")); 779 + close(fd); 911 780 912 - debug_printf("CPU %d\n", cpu); 913 - 914 - if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { 915 - value = req_data; 916 - write = 1; 917 - } 918 - 919 - switch (sub_command) { 920 - case CLOS_PQR_ASSOC: 921 - core_id = parameter & 0xff; 922 - ret = isst_send_mmio_command( 923 - cpu, PQR_ASSOC_OFFSET + core_id * 4, write, 924 - &value); 925 - if (!ret && !write) 926 - *resp = value; 927 - break; 928 - case CLOS_PM_CLOS: 929 - clos_id = parameter & 0x03; 930 - ret = isst_send_mmio_command( 931 - cpu, PM_CLOS_OFFSET + clos_id * 4, write, 932 - &value); 933 - if (!ret && !write) 934 - *resp = value; 935 - break; 936 - case CLOS_STATUS: 937 - break; 938 - default: 939 - break; 940 - } 781 + if (ret == -1) { 782 + debug_printf("Can't activate cpuset controller: Write failed\n"); 941 783 return ret; 942 784 } 943 785 944 - mbox_cmds.cmd_count = 1; 945 - mbox_cmds.mbox_cmd[0].logical_cpu = cpu; 946 - mbox_cmds.mbox_cmd[0].command = command; 947 - mbox_cmds.mbox_cmd[0].sub_command = sub_command; 948 - mbox_cmds.mbox_cmd[0].parameter = parameter; 949 - mbox_cmds.mbox_cmd[0].req_data = req_data; 950 - 951 - if (mbox_delay) 952 - usleep(mbox_delay * 1000); 953 - 954 - fd = open(pathname, O_RDWR); 955 - if (fd < 0) 956 - err(-1, "%s open failed", pathname); 957 - 958 - retry = mbox_retries; 959 - 960 - do { 961 - if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { 962 - if (errno == ENOTTY) { 963 - perror("ISST_IF_MBOX_COMMAND\n"); 964 - fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); 965 - exit(0); 966 - } 967 - debug_printf( 968 - "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", 969 - cpu, command, sub_command, parameter, req_data, errno); 970 - --retry; 971 - } else { 972 - *resp = mbox_cmds.mbox_cmd[0].resp_data; 973 - debug_printf( 974 - "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", 975 - cpu, command, sub_command, parameter, req_data, *resp); 976 - break; 977 - } 978 - } while (retry); 979 - 980 - close(fd); 981 - 982 - if (!retry) { 983 - debug_printf("Failed mbox command even after retries\n"); 984 - return -1; 985 - 986 - } 987 786 return 0; 988 787 } 989 788 990 - int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, 991 - unsigned long long *req_resp) 789 + int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level) 992 790 { 993 - struct isst_if_msr_cmds msr_cmds; 994 - const char *pathname = "/dev/isst_interface"; 995 - int fd; 791 + int i, first, curr_index, index, ret, fd; 792 + static char str[512], dir_name[64]; 793 + static char cpuset_cpus[128]; 794 + int str_len = sizeof(str); 795 + DIR *dir; 996 796 997 - fd = open(pathname, O_RDWR); 998 - if (fd < 0) 999 - err(-1, "%s open failed", pathname); 797 + snprintf(dir_name, sizeof(dir_name), "/sys/fs/cgroup/%d-%d-%d", id->pkg, id->die, id->punit); 798 + dir = opendir(dir_name); 799 + if (!dir) { 800 + ret = mkdir(dir_name, 0744); 801 + if (ret) { 802 + debug_printf("Can't create dir:%s errno:%d\n", dir_name, errno); 803 + return ret; 804 + } 805 + } 806 + closedir(dir); 1000 807 1001 - msr_cmds.cmd_count = 1; 1002 - msr_cmds.msr_cmd[0].logical_cpu = cpu; 1003 - msr_cmds.msr_cmd[0].msr = msr; 1004 - msr_cmds.msr_cmd[0].read_write = write; 1005 - if (write) 1006 - msr_cmds.msr_cmd[0].data = *req_resp; 808 + if (!level) { 809 + sprintf(cpuset_cpus, "%s/cpuset.cpus.partition", dir_name); 1007 810 1008 - if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) { 1009 - perror("ISST_IF_MSR_COMMAND"); 1010 - fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n", 1011 - cpu, msr, write); 1012 - } else { 1013 - if (!write) 1014 - *req_resp = msr_cmds.msr_cmd[0].data; 811 + fd = open(cpuset_cpus, O_RDWR, 0); 812 + if (fd < 0) { 813 + return fd; 814 + } 1015 815 1016 - debug_printf( 1017 - "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n", 1018 - cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data); 816 + ret = write(fd, "member", strlen("member")); 817 + if (ret == -1) { 818 + printf("Can't update to member\n"); 819 + return ret; 820 + } 821 + 822 + return 0; 823 + } 824 + 825 + if (!CPU_COUNT_S(mask_size, cpu_mask)) { 826 + return -1; 827 + } 828 + 829 + curr_index = 0; 830 + first = 1; 831 + str[0] = '\0'; 832 + for (i = 0; i < get_topo_max_cpus(); ++i) { 833 + if (!is_cpu_in_power_domain(i, id)) 834 + continue; 835 + 836 + if (CPU_ISSET_S(i, mask_size, cpu_mask)) 837 + continue; 838 + 839 + if (!first) { 840 + index = snprintf(&str[curr_index], 841 + str_len - curr_index, ","); 842 + curr_index += index; 843 + if (curr_index >= str_len) 844 + break; 845 + } 846 + index = snprintf(&str[curr_index], str_len - curr_index, "%d", 847 + i); 848 + curr_index += index; 849 + if (curr_index >= str_len) 850 + break; 851 + first = 0; 852 + } 853 + 854 + debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str); 855 + 856 + snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name); 857 + 858 + fd = open(cpuset_cpus, O_RDWR, 0); 859 + if (fd < 0) { 860 + return fd; 861 + } 862 + 863 + ret = write(fd, str, strlen(str)); 864 + close(fd); 865 + 866 + if (ret == -1) { 867 + debug_printf("Can't activate cpuset controller: Write failed\n"); 868 + return ret; 869 + } 870 + 871 + snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus.partition", dir_name); 872 + 873 + fd = open(cpuset_cpus, O_RDWR, 0); 874 + if (fd < 0) { 875 + return fd; 876 + } 877 + 878 + ret = write(fd, "isolated", strlen("isolated")); 879 + if (ret == -1) { 880 + debug_printf("Can't update to isolated\n"); 881 + ret = write(fd, "root", strlen("root")); 882 + if (ret == -1) 883 + debug_printf("Can't update to root\n"); 1019 884 } 1020 885 1021 886 close(fd); 887 + 888 + if (ret < 0) 889 + return ret; 1022 890 1023 891 return 0; 1024 892 } ··· 980 942 { 981 943 const char *pathname = "/dev/isst_interface"; 982 944 int fd; 945 + 946 + if (is_clx_n_platform()) { 947 + isst_platform_info.api_version = 1; 948 + goto set_platform_ops; 949 + } 983 950 984 951 fd = open(pathname, O_RDWR); 985 952 if (fd < 0) ··· 1002 959 printf("Incompatible API versions; Upgrade of tool is required\n"); 1003 960 return -1; 1004 961 } 962 + 963 + set_platform_ops: 964 + if (isst_set_platform_ops(isst_platform_info.api_version)) { 965 + fprintf(stderr, "Failed to set platform callbacks\n"); 966 + exit(0); 967 + } 1005 968 return 0; 969 + } 970 + 971 + void get_isst_status(struct isst_id *id, void *arg1, void *arg2, void *arg3, void *arg4) 972 + { 973 + struct isst_pkg_ctdp pkg_dev; 974 + struct isst_id *tid = (struct isst_id *)arg2; 975 + int *mask = (int *)arg3; 976 + int *max_level = (int *)arg4; 977 + int j, ret; 978 + 979 + /* Only check the first cpu power domain */ 980 + if (id->cpu < 0 || tid->cpu >= 0) 981 + return; 982 + 983 + ret = isst_get_ctdp_levels(id, &pkg_dev); 984 + if (ret) 985 + return; 986 + 987 + if (pkg_dev.enabled) 988 + *mask |= BIT(0); 989 + 990 + if (pkg_dev.locked) 991 + *mask |= BIT(1); 992 + 993 + if (*max_level < pkg_dev.levels) 994 + *max_level = pkg_dev.levels; 995 + 996 + for (j = 0; j <= pkg_dev.levels; ++j) { 997 + struct isst_pkg_ctdp_level_info ctdp_level; 998 + 999 + ret = isst_get_ctdp_control(id, j, &ctdp_level); 1000 + if (ret) 1001 + continue; 1002 + 1003 + if (ctdp_level.fact_support) 1004 + *mask |= BIT(2); 1005 + 1006 + if (ctdp_level.pbf_support) 1007 + *mask |= BIT(3); 1008 + } 1009 + 1010 + tid->cpu = id->cpu; 1011 + tid->pkg = id->pkg; 1012 + tid->die = id->die; 1013 + tid->punit = id->punit; 1006 1014 } 1007 1015 1008 1016 static void isst_print_extended_platform_info(void) 1009 1017 { 1010 - int cp_state, cp_cap, fact_support = 0, pbf_support = 0; 1011 - struct isst_pkg_ctdp_level_info ctdp_level; 1012 - struct isst_pkg_ctdp pkg_dev; 1013 - int ret, i, j; 1014 - FILE *filep; 1018 + int cp_state, cp_cap; 1015 1019 struct isst_id id; 1020 + int mask = 0, max_level = 0; 1016 1021 1017 - for (i = 0; i < 256; ++i) { 1018 - char path[256]; 1022 + id.cpu = -1; 1023 + for_each_online_power_domain_in_set(get_isst_status, NULL, &id, &mask, &max_level); 1019 1024 1020 - snprintf(path, sizeof(path), 1021 - "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i); 1022 - filep = fopen(path, "r"); 1023 - if (filep) 1024 - break; 1025 - } 1026 - 1027 - if (!filep) 1028 - return; 1029 - 1030 - fclose(filep); 1031 - 1032 - set_isst_id(&id, i); 1033 - ret = isst_get_ctdp_levels(&id, &pkg_dev); 1034 - if (ret) 1035 - return; 1036 - 1037 - if (pkg_dev.enabled) { 1025 + if (mask & BIT(0)) { 1038 1026 fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is supported\n"); 1039 1027 } else { 1040 1028 fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is not supported\n"); 1041 1029 fprintf(outf, "Only performance level 0 (base level) is present\n"); 1042 1030 } 1043 1031 1044 - if (pkg_dev.locked) 1032 + if (mask & BIT(1)) 1045 1033 fprintf(outf, "TDP level change control is locked\n"); 1046 1034 else 1047 - fprintf(outf, "TDP level change control is unlocked, max level: %d \n", pkg_dev.levels); 1035 + fprintf(outf, "TDP level change control is unlocked, max level: %d\n", max_level); 1048 1036 1049 - for (j = 0; j <= pkg_dev.levels; ++j) { 1050 - ret = isst_get_ctdp_control(&id, j, &ctdp_level); 1051 - if (ret) 1052 - continue; 1053 - 1054 - if (!fact_support && ctdp_level.fact_support) 1055 - fact_support = 1; 1056 - 1057 - if (!pbf_support && ctdp_level.pbf_support) 1058 - pbf_support = 1; 1059 - } 1060 - 1061 - if (fact_support) 1037 + if (mask & BIT(2)) 1062 1038 fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is supported\n"); 1063 1039 else 1064 1040 fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is not supported\n"); 1065 1041 1066 - if (pbf_support) 1042 + if (mask & BIT(3)) 1067 1043 fprintf(outf, "Intel(R) SST-BF (feature base-freq) is supported\n"); 1068 1044 else 1069 1045 fprintf(outf, "Intel(R) SST-BF (feature base-freq) is not supported\n"); 1070 1046 1071 - ret = isst_read_pm_config(&id, &cp_state, &cp_cap); 1072 - if (ret) { 1047 + if (isst_read_pm_config(&id, &cp_state, &cp_cap)) { 1073 1048 fprintf(outf, "Intel(R) SST-CP (feature core-power) status is unknown\n"); 1074 1049 return; 1075 1050 } 1051 + 1076 1052 if (cp_cap) 1077 1053 fprintf(outf, "Intel(R) SST-CP (feature core-power) is supported\n"); 1078 1054 else ··· 1100 1038 1101 1039 static void isst_print_platform_information(void) 1102 1040 { 1103 - struct isst_if_platform_info platform_info; 1104 - const char *pathname = "/dev/isst_interface"; 1105 - int fd; 1106 - 1107 1041 if (is_clx_n_platform()) { 1108 1042 fprintf(stderr, "\nThis option in not supported on this platform\n"); 1109 1043 exit(0); ··· 1109 1051 set_max_cpu_num(); 1110 1052 create_cpu_map(); 1111 1053 1112 - fd = open(pathname, O_RDWR); 1113 - if (fd < 0) 1114 - err(-1, "%s open failed", pathname); 1115 - 1116 - if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &platform_info) == -1) { 1117 - perror("ISST_IF_GET_PLATFORM_INFO"); 1118 - } else { 1119 - fprintf(outf, "Platform: API version : %d\n", 1120 - platform_info.api_version); 1121 - fprintf(outf, "Platform: Driver version : %d\n", 1122 - platform_info.driver_version); 1123 - fprintf(outf, "Platform: mbox supported : %d\n", 1124 - platform_info.mbox_supported); 1125 - fprintf(outf, "Platform: mmio supported : %d\n", 1126 - platform_info.mmio_supported); 1127 - isst_print_extended_platform_info(); 1128 - } 1129 - 1130 - close(fd); 1054 + fprintf(outf, "Platform: API version : %d\n", 1055 + isst_platform_info.api_version); 1056 + fprintf(outf, "Platform: Driver version : %d\n", 1057 + isst_platform_info.driver_version); 1058 + fprintf(outf, "Platform: mbox supported : %d\n", 1059 + isst_platform_info.mbox_supported); 1060 + fprintf(outf, "Platform: mmio supported : %d\n", 1061 + isst_platform_info.mmio_supported); 1062 + isst_print_extended_platform_info(); 1131 1063 1132 1064 exit(0); 1133 1065 } ··· 1158 1110 exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix, \ 1159 1111 &ctdp, desc, &ctdp.object); \ 1160 1112 else \ 1161 - for_each_online_package_in_set(exec_on_get_ctdp_cpu, \ 1113 + for_each_online_power_domain_in_set(exec_on_get_ctdp_cpu, \ 1162 1114 isst_get_ctdp_##suffix, \ 1163 1115 &ctdp, desc, \ 1164 1116 &ctdp.object); \ ··· 1362 1314 if (max_target_cpus) 1363 1315 for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); 1364 1316 else 1365 - for_each_online_package_in_set(fn, NULL, NULL, NULL, NULL); 1317 + for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL); 1366 1318 1367 1319 isst_ctdp_display_information_end(outf); 1368 - } 1369 - 1370 - static int set_uncore_min_max(struct isst_id *id, int max, int freq) 1371 - { 1372 - char buffer[128], freq_str[16]; 1373 - int fd, ret, len; 1374 - 1375 - if (max) 1376 - snprintf(buffer, sizeof(buffer), 1377 - "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/max_freq_khz", id->pkg, id->die); 1378 - else 1379 - snprintf(buffer, sizeof(buffer), 1380 - "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/min_freq_khz", id->pkg, id->die); 1381 - 1382 - fd = open(buffer, O_WRONLY); 1383 - if (fd < 0) 1384 - return fd; 1385 - 1386 - snprintf(freq_str, sizeof(freq_str), "%d", freq); 1387 - len = strlen(freq_str); 1388 - ret = write(fd, freq_str, len); 1389 - if (ret == -1) { 1390 - close(fd); 1391 - return ret; 1392 - } 1393 - close(fd); 1394 - 1395 - return 0; 1396 1320 } 1397 1321 1398 1322 static void adjust_scaling_max_from_base_freq(int cpu); ··· 1372 1352 static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, 1373 1353 void *arg4) 1374 1354 { 1355 + struct isst_pkg_ctdp pkg_dev; 1375 1356 int ret; 1357 + 1358 + ret = isst_get_ctdp_levels(id, &pkg_dev); 1359 + if (ret) { 1360 + isst_display_error_info_message(1, "Get TDP level failed", 0, 0); 1361 + isst_ctdp_display_information_end(outf); 1362 + exit(1); 1363 + } 1364 + 1365 + if (pkg_dev.current_level == tdp_level) { 1366 + debug_printf("TDP level already set. Skipped\n"); 1367 + goto display_result; 1368 + } 1376 1369 1377 1370 ret = isst_set_tdp_level(id, tdp_level); 1378 1371 if (ret) { 1379 1372 isst_display_error_info_message(1, "Set TDP level failed", 0, 0); 1380 1373 isst_ctdp_display_information_end(outf); 1381 1374 exit(1); 1382 - } else { 1383 - isst_display_result(id, outf, "perf-profile", "set_tdp_level", 1384 - ret); 1385 - if (force_online_offline) { 1386 - struct isst_pkg_ctdp_level_info ctdp_level; 1375 + } 1387 1376 1388 - /* Wait for updated base frequencies */ 1389 - usleep(2000); 1377 + display_result: 1378 + isst_display_result(id, outf, "perf-profile", "set_tdp_level", ret); 1379 + if (force_online_offline && id->cpu >= 0) { 1380 + struct isst_pkg_ctdp_level_info ctdp_level; 1390 1381 1391 - /* Adjusting uncore freq */ 1392 - isst_get_uncore_p0_p1_info(id, tdp_level, &ctdp_level); 1393 - if (ctdp_level.uncore_pm) 1394 - set_uncore_min_max(id, 0, ctdp_level.uncore_pm * 100000); 1382 + /* Wait for updated base frequencies */ 1383 + usleep(2000); 1395 1384 1396 - if (ctdp_level.uncore_p0) 1397 - set_uncore_min_max(id, 1, ctdp_level.uncore_p0 * 100000); 1385 + /* Adjusting uncore freq */ 1386 + isst_adjust_uncore_freq(id, tdp_level, &ctdp_level); 1398 1387 1399 - fprintf(stderr, "Option is set to online/offline\n"); 1400 - ctdp_level.core_cpumask_size = 1401 - alloc_cpu_set(&ctdp_level.core_cpumask); 1402 - ret = isst_get_coremask_info(id, tdp_level, &ctdp_level); 1403 - if (ret) { 1404 - isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); 1405 - return; 1406 - } 1407 - if (ctdp_level.cpu_count) { 1408 - int i, max_cpus = get_topo_max_cpus(); 1409 - for (i = 0; i < max_cpus; ++i) { 1410 - if (!is_cpu_in_power_domain(i, id)) 1411 - continue; 1412 - if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { 1413 - fprintf(stderr, "online cpu %d\n", i); 1414 - set_cpu_online_offline(i, 1); 1415 - adjust_scaling_max_from_base_freq(i); 1416 - } else { 1417 - fprintf(stderr, "offline cpu %d\n", i); 1418 - set_cpu_online_offline(i, 0); 1419 - } 1388 + fprintf(stderr, "Option is set to online/offline\n"); 1389 + ctdp_level.core_cpumask_size = 1390 + alloc_cpu_set(&ctdp_level.core_cpumask); 1391 + ret = isst_get_coremask_info(id, tdp_level, &ctdp_level); 1392 + if (ret) { 1393 + isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); 1394 + goto free_mask; 1395 + } 1396 + 1397 + if (use_cgroupv2()) { 1398 + int ret; 1399 + 1400 + fprintf(stderr, "Using cgroup v2 in lieu of online/offline\n"); 1401 + ret = enable_cpuset_controller(); 1402 + if (ret) 1403 + goto use_offline; 1404 + 1405 + ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level); 1406 + if (ret) 1407 + goto use_offline; 1408 + 1409 + goto free_mask; 1410 + } 1411 + 1412 + use_offline: 1413 + if (ctdp_level.cpu_count) { 1414 + int i, max_cpus = get_topo_max_cpus(); 1415 + for (i = 0; i < max_cpus; ++i) { 1416 + if (!is_cpu_in_power_domain(i, id)) 1417 + continue; 1418 + if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { 1419 + fprintf(stderr, "online cpu %d\n", i); 1420 + set_cpu_online_offline(i, 1); 1421 + adjust_scaling_max_from_base_freq(i); 1422 + } else { 1423 + fprintf(stderr, "offline cpu %d\n", i); 1424 + set_cpu_online_offline(i, 0); 1420 1425 } 1421 1426 } 1422 1427 } 1428 + free_mask: 1429 + free_cpu_set(ctdp_level.core_cpumask); 1423 1430 } 1424 1431 } 1425 1432 ··· 1472 1425 for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL, 1473 1426 NULL, NULL, NULL); 1474 1427 else 1475 - for_each_online_package_in_set(set_tdp_level_for_cpu, NULL, 1428 + for_each_online_power_domain_in_set(set_tdp_level_for_cpu, NULL, 1476 1429 NULL, NULL, NULL); 1477 1430 isst_ctdp_display_information_end(outf); 1478 1431 } ··· 1510 1463 exit(1); 1511 1464 } else { 1512 1465 isst_pbf_display_information(id, outf, tdp_level, &pbf_info); 1513 - isst_get_pbf_info_complete(&pbf_info); 1466 + free_cpu_set(pbf_info.core_cpumask); 1514 1467 } 1515 1468 } 1516 1469 ··· 1541 1494 if (max_target_cpus) 1542 1495 for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); 1543 1496 else 1544 - for_each_online_package_in_set(fn, NULL, NULL, NULL, NULL); 1497 + for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL); 1545 1498 1546 1499 isst_ctdp_display_information_end(outf); 1547 1500 } ··· 1709 1662 { 1710 1663 int i; 1711 1664 1665 + if (id->cpu < 0) 1666 + return; 1667 + 1712 1668 for (i = 0; i < get_topo_max_cpus(); ++i) { 1713 1669 if (!is_cpu_in_power_domain(i, id)) 1714 1670 continue; ··· 1728 1678 static void set_scaling_min_to_cpuinfo_min(struct isst_id *id) 1729 1679 { 1730 1680 int i; 1681 + 1682 + if (id->cpu < 0) 1683 + return; 1731 1684 1732 1685 for (i = 0; i < get_topo_max_cpus(); ++i) { 1733 1686 if (!is_cpu_in_power_domain(i, id)) ··· 1810 1757 struct isst_pbf_info pbf_info; 1811 1758 struct isst_pkg_ctdp pkg_dev; 1812 1759 int ret; 1760 + 1761 + if (id->cpu < 0) 1762 + return 0; 1813 1763 1814 1764 ret = isst_get_ctdp_levels(id, &pkg_dev); 1815 1765 if (ret) { ··· 1956 1900 for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL, 1957 1901 NULL, &enable); 1958 1902 else 1959 - for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL, 1903 + for_each_online_power_domain_in_set(set_pbf_for_cpu, NULL, NULL, 1960 1904 NULL, &enable); 1961 1905 isst_ctdp_display_information_end(outf); 1962 1906 } ··· 2002 1946 for_each_online_target_cpu_in_set(dump_fact_config_for_cpu, 2003 1947 NULL, NULL, NULL, NULL); 2004 1948 else 2005 - for_each_online_package_in_set(dump_fact_config_for_cpu, NULL, 1949 + for_each_online_power_domain_in_set(dump_fact_config_for_cpu, NULL, 2006 1950 NULL, NULL, NULL); 2007 1951 isst_ctdp_display_information_end(outf); 2008 1952 } ··· 2059 2003 struct isst_pkg_ctdp pkg_dev; 2060 2004 2061 2005 ret = isst_get_ctdp_levels(id, &pkg_dev); 2062 - if (!ret) 2006 + if (!ret && id->cpu >= 0) 2063 2007 ret = isst_set_trl(id, fact_trl); 2064 2008 if (ret && auto_mode) 2065 2009 isst_pm_qos_config(id, 0, 0); ··· 2111 2055 for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL, 2112 2056 NULL, &enable); 2113 2057 else 2114 - for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL, 2058 + for_each_online_power_domain_in_set(set_fact_for_cpu, NULL, NULL, 2115 2059 NULL, &enable); 2116 2060 isst_ctdp_display_information_end(outf); 2117 2061 ··· 2250 2194 for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL, 2251 2195 NULL, NULL, &enable); 2252 2196 else 2253 - for_each_online_package_in_set(enable_clos_qos_config, NULL, 2197 + for_each_online_power_domain_in_set(enable_clos_qos_config, NULL, 2254 2198 NULL, NULL, &enable); 2255 2199 isst_ctdp_display_information_end(outf); 2256 2200 } ··· 2260 2204 { 2261 2205 struct isst_clos_config clos_config; 2262 2206 int ret; 2207 + 2208 + if (id->cpu < 0) 2209 + return; 2263 2210 2264 2211 ret = isst_pm_get_clos(id, current_clos, &clos_config); 2265 2212 if (ret) ··· 2292 2233 for_each_online_target_cpu_in_set(dump_clos_config_for_cpu, 2293 2234 NULL, NULL, NULL, NULL); 2294 2235 else 2295 - for_each_online_package_in_set(dump_clos_config_for_cpu, NULL, 2236 + for_each_online_power_domain_in_set(dump_clos_config_for_cpu, NULL, 2296 2237 NULL, NULL, NULL); 2297 2238 isst_ctdp_display_information_end(outf); 2298 2239 } ··· 2328 2269 for_each_online_target_cpu_in_set(get_clos_info_for_cpu, NULL, 2329 2270 NULL, NULL, NULL); 2330 2271 else 2331 - for_each_online_package_in_set(get_clos_info_for_cpu, NULL, 2272 + for_each_online_power_domain_in_set(get_clos_info_for_cpu, NULL, 2332 2273 NULL, NULL, NULL); 2333 2274 isst_ctdp_display_information_end(outf); 2334 2275 ··· 2339 2280 { 2340 2281 struct isst_clos_config clos_config; 2341 2282 int ret; 2283 + 2284 + if (id->cpu < 0) 2285 + return; 2342 2286 2343 2287 clos_config.epp = clos_epp; 2344 2288 clos_config.clos_prop_prio = clos_prop_prio; ··· 2403 2341 for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL, 2404 2342 NULL, NULL, NULL); 2405 2343 else 2406 - for_each_online_package_in_set(set_clos_config_for_cpu, NULL, 2344 + for_each_online_power_domain_in_set(set_clos_config_for_cpu, NULL, 2407 2345 NULL, NULL, NULL); 2408 2346 isst_ctdp_display_information_end(outf); 2409 2347 } ··· 2570 2508 for_each_online_target_cpu_in_set(get_set_trl, NULL, 2571 2509 NULL, NULL, &arg); 2572 2510 else 2573 - for_each_online_package_in_set(get_set_trl, NULL, 2511 + for_each_online_power_domain_in_set(get_set_trl, NULL, 2574 2512 NULL, NULL, &arg); 2575 2513 isst_ctdp_display_information_end(outf); 2576 2514 } ··· 2745 2683 break; 2746 2684 case 'd': 2747 2685 clos_desired = atoi(optarg); 2748 - clos_desired /= DISP_FREQ_MULTIPLIER; 2686 + clos_desired /= isst_get_disp_freq_multiplier(); 2749 2687 break; 2750 2688 case 'e': 2751 2689 clos_epp = atoi(optarg); ··· 2756 2694 break; 2757 2695 case 'n': 2758 2696 clos_min = atoi(optarg); 2759 - clos_min /= DISP_FREQ_MULTIPLIER; 2697 + clos_min /= isst_get_disp_freq_multiplier(); 2760 2698 break; 2761 2699 case 'm': 2762 2700 clos_max = atoi(optarg); 2763 - clos_max /= DISP_FREQ_MULTIPLIER; 2701 + clos_max /= isst_get_disp_freq_multiplier(); 2764 2702 break; 2765 2703 case 'p': 2766 2704 clos_priority_type = atoi(optarg); ··· 2944 2882 printf("\t[-b|--oob : Start a daemon to process HFI events for perf profile change from Out of Band agent.\n"); 2945 2883 printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n"); 2946 2884 printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n"); 2885 + printf("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\n"); 2947 2886 printf("\nResult format\n"); 2948 2887 printf("\tResult display uses a common format for each command:\n"); 2949 2888 printf("\tResults are formatted in text/JSON with\n"); ··· 2981 2918 int oob_mode = 0; 2982 2919 int poll_interval = -1; 2983 2920 int no_daemon = 0; 2921 + int mbox_delay = 0, mbox_retries = 3; 2984 2922 2985 2923 static struct option long_options[] = { 2986 2924 { "all-cpus-online", no_argument, 0, 'a' }, ··· 2997 2933 { "oob", no_argument, 0, 'b' }, 2998 2934 { "no-daemon", no_argument, 0, 'n' }, 2999 2935 { "poll-interval", required_argument, 0, 'w' }, 2936 + { "cgroupv2", required_argument, 0, 'g' }, 3000 2937 { 0, 0, 0, 0 } 3001 2938 }; 3002 2939 ··· 3023 2958 fclose(fp); 3024 2959 } 3025 2960 2961 + ret = isst_fill_platform_info(); 2962 + if (ret) 2963 + goto out; 2964 + 3026 2965 progname = argv[0]; 3027 - while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:n", long_options, 2966 + while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options, 3028 2967 &option_index)) != -1) { 3029 2968 switch (opt) { 3030 2969 case 'a': ··· 3087 3018 } 3088 3019 poll_interval = ret; 3089 3020 break; 3021 + case 'g': 3022 + cgroupv2 = 1; 3023 + break; 3090 3024 default: 3091 3025 usage(); 3092 3026 } ··· 3099 3027 usage(); 3100 3028 exit(0); 3101 3029 } 3030 + 3031 + isst_update_platform_param(ISST_PARAM_MBOX_DELAY, mbox_delay); 3032 + isst_update_platform_param(ISST_PARAM_MBOX_RETRIES, mbox_retries); 3033 + 3102 3034 set_max_cpu_num(); 3103 3035 if (force_cpus_online) 3104 3036 force_all_cpus_online(); ··· 3120 3044 } 3121 3045 3122 3046 if (!is_clx_n_platform()) { 3123 - ret = isst_fill_platform_info(); 3124 - if (ret) 3125 - goto out; 3126 3047 process_command(argc, argv, isst_help_cmds, isst_cmds); 3127 3048 } else { 3128 3049 process_command(argc, argv, clx_n_help_cmds, clx_n_cmds);
+1066
tools/power/x86/intel-speed-select/isst-core-mbox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Speed Select -- Enumerate and control features for Mailbox Interface 4 + * Copyright (c) 2023 Intel Corporation. 5 + */ 6 + #include "isst.h" 7 + 8 + static int mbox_delay; 9 + static int mbox_retries = 3; 10 + 11 + #define MAX_TRL_LEVELS_EMR 5 12 + 13 + static int mbox_get_disp_freq_multiplier(void) 14 + { 15 + return DISP_FREQ_MULTIPLIER; 16 + } 17 + 18 + static int mbox_get_trl_max_levels(void) 19 + { 20 + if (is_emr_platform()) 21 + return MAX_TRL_LEVELS_EMR; 22 + 23 + return 3; 24 + } 25 + 26 + static char *mbox_get_trl_level_name(int level) 27 + { 28 + if (is_emr_platform()) { 29 + static char level_str[18]; 30 + 31 + if (level >= MAX_TRL_LEVELS_EMR) 32 + return NULL; 33 + 34 + snprintf(level_str, sizeof(level_str), "level-%d", level); 35 + return level_str; 36 + } 37 + 38 + switch (level) { 39 + case 0: 40 + return "sse"; 41 + case 1: 42 + return "avx2"; 43 + case 2: 44 + return "avx512"; 45 + default: 46 + return NULL; 47 + } 48 + } 49 + 50 + static void mbox_update_platform_param(enum isst_platform_param param, int value) 51 + { 52 + switch (param) { 53 + case ISST_PARAM_MBOX_DELAY: 54 + mbox_delay = value; 55 + break; 56 + case ISST_PARAM_MBOX_RETRIES: 57 + mbox_retries = value; 58 + break; 59 + default: 60 + break; 61 + } 62 + } 63 + 64 + static int mbox_is_punit_valid(struct isst_id *id) 65 + { 66 + if (id->cpu < 0) 67 + return 0; 68 + 69 + if (id->pkg < 0 || id->die < 0 || id->punit) 70 + return 0; 71 + 72 + return 1; 73 + } 74 + 75 + static int _send_mmio_command(unsigned int cpu, unsigned int reg, int write, 76 + unsigned int *value) 77 + { 78 + struct isst_if_io_regs io_regs; 79 + const char *pathname = "/dev/isst_interface"; 80 + int cmd; 81 + FILE *outf = get_output_file(); 82 + int fd; 83 + 84 + debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); 85 + 86 + fd = open(pathname, O_RDWR); 87 + if (fd < 0) 88 + err(-1, "%s open failed", pathname); 89 + 90 + io_regs.req_count = 1; 91 + io_regs.io_reg[0].logical_cpu = cpu; 92 + io_regs.io_reg[0].reg = reg; 93 + cmd = ISST_IF_IO_CMD; 94 + if (write) { 95 + io_regs.io_reg[0].read_write = 1; 96 + io_regs.io_reg[0].value = *value; 97 + } else { 98 + io_regs.io_reg[0].read_write = 0; 99 + } 100 + 101 + if (ioctl(fd, cmd, &io_regs) == -1) { 102 + if (errno == ENOTTY) { 103 + perror("ISST_IF_IO_COMMAND\n"); 104 + fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); 105 + exit(0); 106 + } 107 + fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", 108 + cpu, reg, write); 109 + } else { 110 + if (!write) 111 + *value = io_regs.io_reg[0].value; 112 + 113 + debug_printf( 114 + "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", 115 + cpu, reg, write, *value); 116 + } 117 + 118 + close(fd); 119 + 120 + return 0; 121 + } 122 + 123 + int _send_mbox_command(unsigned int cpu, unsigned char command, 124 + unsigned char sub_command, unsigned int parameter, 125 + unsigned int req_data, unsigned int *resp) 126 + { 127 + const char *pathname = "/dev/isst_interface"; 128 + int fd, retry; 129 + struct isst_if_mbox_cmds mbox_cmds = { 0 }; 130 + 131 + debug_printf( 132 + "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", 133 + cpu, command, sub_command, parameter, req_data); 134 + 135 + if (!is_skx_based_platform() && command == CONFIG_CLOS && 136 + sub_command != CLOS_PM_QOS_CONFIG) { 137 + unsigned int value; 138 + int write = 0; 139 + int clos_id, core_id, ret = 0; 140 + 141 + debug_printf("CPU %d\n", cpu); 142 + 143 + if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { 144 + value = req_data; 145 + write = 1; 146 + } 147 + 148 + switch (sub_command) { 149 + case CLOS_PQR_ASSOC: 150 + core_id = parameter & 0xff; 151 + ret = _send_mmio_command( 152 + cpu, PQR_ASSOC_OFFSET + core_id * 4, write, 153 + &value); 154 + if (!ret && !write) 155 + *resp = value; 156 + break; 157 + case CLOS_PM_CLOS: 158 + clos_id = parameter & 0x03; 159 + ret = _send_mmio_command( 160 + cpu, PM_CLOS_OFFSET + clos_id * 4, write, 161 + &value); 162 + if (!ret && !write) 163 + *resp = value; 164 + break; 165 + case CLOS_STATUS: 166 + break; 167 + default: 168 + break; 169 + } 170 + return ret; 171 + } 172 + 173 + mbox_cmds.cmd_count = 1; 174 + mbox_cmds.mbox_cmd[0].logical_cpu = cpu; 175 + mbox_cmds.mbox_cmd[0].command = command; 176 + mbox_cmds.mbox_cmd[0].sub_command = sub_command; 177 + mbox_cmds.mbox_cmd[0].parameter = parameter; 178 + mbox_cmds.mbox_cmd[0].req_data = req_data; 179 + 180 + if (mbox_delay) 181 + usleep(mbox_delay * 1000); 182 + 183 + fd = open(pathname, O_RDWR); 184 + if (fd < 0) 185 + err(-1, "%s open failed", pathname); 186 + 187 + retry = mbox_retries; 188 + do { 189 + if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { 190 + if (errno == ENOTTY) { 191 + perror("ISST_IF_MBOX_COMMAND\n"); 192 + fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); 193 + exit(0); 194 + } 195 + debug_printf( 196 + "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", 197 + cpu, command, sub_command, parameter, req_data, errno); 198 + --retry; 199 + } else { 200 + *resp = mbox_cmds.mbox_cmd[0].resp_data; 201 + debug_printf( 202 + "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", 203 + cpu, command, sub_command, parameter, req_data, *resp); 204 + break; 205 + } 206 + } while (retry); 207 + 208 + close(fd); 209 + 210 + if (!retry) { 211 + debug_printf("Failed mbox command even after retries\n"); 212 + return -1; 213 + 214 + } 215 + 216 + return 0; 217 + } 218 + 219 + static int mbox_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) 220 + { 221 + unsigned int resp; 222 + int ret; 223 + 224 + ret = _send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, 225 + &resp); 226 + if (ret) 227 + return ret; 228 + 229 + debug_printf("cpu:%d READ_PM_CONFIG resp:%x\n", id->cpu, resp); 230 + 231 + *cp_state = resp & BIT(16); 232 + *cp_cap = resp & BIT(0) ? 1 : 0; 233 + 234 + return 0; 235 + } 236 + 237 + static int mbox_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) 238 + { 239 + unsigned int resp; 240 + int ret; 241 + 242 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 243 + CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); 244 + if (ret) { 245 + pkg_dev->levels = 0; 246 + pkg_dev->locked = 1; 247 + pkg_dev->current_level = 0; 248 + pkg_dev->version = 0; 249 + pkg_dev->enabled = 0; 250 + return 0; 251 + } 252 + 253 + debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", id->cpu, resp); 254 + 255 + pkg_dev->version = resp & 0xff; 256 + pkg_dev->levels = (resp >> 8) & 0xff; 257 + pkg_dev->current_level = (resp >> 16) & 0xff; 258 + pkg_dev->locked = !!(resp & BIT(24)); 259 + pkg_dev->enabled = !!(resp & BIT(31)); 260 + 261 + return 0; 262 + } 263 + 264 + static int mbox_get_ctdp_control(struct isst_id *id, int config_index, 265 + struct isst_pkg_ctdp_level_info *ctdp_level) 266 + { 267 + int cp_state, cp_cap; 268 + unsigned int resp; 269 + int ret; 270 + 271 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 272 + CONFIG_TDP_GET_TDP_CONTROL, 0, 273 + config_index, &resp); 274 + if (ret) 275 + return ret; 276 + 277 + ctdp_level->fact_support = resp & BIT(0); 278 + ctdp_level->pbf_support = !!(resp & BIT(1)); 279 + ctdp_level->fact_enabled = !!(resp & BIT(16)); 280 + ctdp_level->pbf_enabled = !!(resp & BIT(17)); 281 + 282 + ret = isst_read_pm_config(id, &cp_state, &cp_cap); 283 + if (ret) { 284 + debug_printf("cpu:%d pm_config is not supported\n", id->cpu); 285 + } else { 286 + debug_printf("cpu:%d pm_config SST-CP state:%d cap:%d\n", id->cpu, cp_state, cp_cap); 287 + ctdp_level->sst_cp_support = cp_cap; 288 + ctdp_level->sst_cp_enabled = cp_state; 289 + } 290 + 291 + debug_printf( 292 + "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", 293 + id->cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support, 294 + ctdp_level->fact_enabled, ctdp_level->pbf_enabled); 295 + 296 + return 0; 297 + } 298 + 299 + static void _get_uncore_p0_p1_info(struct isst_id *id, int config_index, 300 + struct isst_pkg_ctdp_level_info *ctdp_level) 301 + { 302 + unsigned int resp; 303 + int ret; 304 + 305 + ctdp_level->uncore_pm = 0; 306 + ctdp_level->uncore_p0 = 0; 307 + ctdp_level->uncore_p1 = 0; 308 + 309 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 310 + CONFIG_TDP_GET_RATIO_INFO, 0, 311 + (BIT(16) | config_index) , &resp); 312 + if (ret) { 313 + goto try_uncore_mbox; 314 + } 315 + 316 + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); 317 + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; 318 + ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; 319 + 320 + debug_printf( 321 + "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", 322 + id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, 323 + ctdp_level->uncore_pm); 324 + 325 + return; 326 + 327 + try_uncore_mbox: 328 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 329 + CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, 330 + config_index, &resp); 331 + if (ret) { 332 + ctdp_level->uncore_p0 = 0; 333 + ctdp_level->uncore_p1 = 0; 334 + return; 335 + } 336 + 337 + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); 338 + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; 339 + debug_printf( 340 + "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", 341 + id->cpu, config_index, resp, ctdp_level->uncore_p0, 342 + ctdp_level->uncore_p1); 343 + } 344 + 345 + static int _set_uncore_min_max(struct isst_id *id, int max, int freq) 346 + { 347 + char buffer[128], freq_str[16]; 348 + int fd, ret, len; 349 + 350 + if (max) 351 + snprintf(buffer, sizeof(buffer), 352 + "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/max_freq_khz", id->pkg, id->die); 353 + else 354 + snprintf(buffer, sizeof(buffer), 355 + "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/min_freq_khz", id->pkg, id->die); 356 + 357 + fd = open(buffer, O_WRONLY); 358 + if (fd < 0) 359 + return fd; 360 + 361 + snprintf(freq_str, sizeof(freq_str), "%d", freq); 362 + len = strlen(freq_str); 363 + ret = write(fd, freq_str, len); 364 + if (ret == -1) { 365 + close(fd); 366 + return ret; 367 + } 368 + close(fd); 369 + 370 + return 0; 371 + } 372 + 373 + static void mbox_adjust_uncore_freq(struct isst_id *id, int config_index, 374 + struct isst_pkg_ctdp_level_info *ctdp_level) 375 + { 376 + _get_uncore_p0_p1_info(id, config_index, ctdp_level); 377 + if (ctdp_level->uncore_pm) 378 + _set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000); 379 + 380 + if (ctdp_level->uncore_p0) 381 + _set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000); 382 + } 383 + 384 + static void _get_p1_info(struct isst_id *id, int config_index, 385 + struct isst_pkg_ctdp_level_info *ctdp_level) 386 + { 387 + unsigned int resp; 388 + int ret; 389 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, 390 + config_index, &resp); 391 + if (ret) { 392 + ctdp_level->sse_p1 = 0; 393 + ctdp_level->avx2_p1 = 0; 394 + ctdp_level->avx512_p1 = 0; 395 + return; 396 + } 397 + 398 + ctdp_level->sse_p1 = resp & GENMASK(7, 0); 399 + ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; 400 + ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; 401 + ctdp_level->amx_p1 = (resp & GENMASK(31, 24)) >> 24; 402 + debug_printf( 403 + "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d amx_p1:%d\n", 404 + id->cpu, config_index, resp, ctdp_level->sse_p1, 405 + ctdp_level->avx2_p1, ctdp_level->avx512_p1, ctdp_level->amx_p1); 406 + } 407 + 408 + static void _get_uncore_mem_freq(struct isst_id *id, int config_index, 409 + struct isst_pkg_ctdp_level_info *ctdp_level) 410 + { 411 + unsigned int resp; 412 + int ret; 413 + 414 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, 415 + 0, config_index, &resp); 416 + if (ret) { 417 + ctdp_level->mem_freq = 0; 418 + return; 419 + } 420 + 421 + ctdp_level->mem_freq = resp & GENMASK(7, 0); 422 + if (is_spr_platform() || is_emr_platform()) { 423 + ctdp_level->mem_freq *= 200; 424 + } else if (is_icx_platform()) { 425 + if (ctdp_level->mem_freq < 7) { 426 + ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; 427 + ctdp_level->mem_freq /= 10; 428 + if (ctdp_level->mem_freq % 10 > 5) 429 + ctdp_level->mem_freq++; 430 + } else { 431 + ctdp_level->mem_freq = 0; 432 + } 433 + } else { 434 + ctdp_level->mem_freq = 0; 435 + } 436 + debug_printf( 437 + "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", 438 + id->cpu, config_index, resp, ctdp_level->mem_freq); 439 + } 440 + 441 + static int mbox_get_tdp_info(struct isst_id *id, int config_index, 442 + struct isst_pkg_ctdp_level_info *ctdp_level) 443 + { 444 + unsigned int resp; 445 + int ret; 446 + 447 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, 448 + 0, config_index, &resp); 449 + if (ret) { 450 + isst_display_error_info_message(1, "Invalid level, Can't get TDP information at level", 1, config_index); 451 + return ret; 452 + } 453 + 454 + ctdp_level->pkg_tdp = resp & GENMASK(14, 0); 455 + ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16; 456 + 457 + debug_printf( 458 + "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n", 459 + id->cpu, config_index, resp, ctdp_level->tdp_ratio, 460 + ctdp_level->pkg_tdp); 461 + 462 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, 463 + 0, config_index, &resp); 464 + if (ret) 465 + return ret; 466 + 467 + ctdp_level->t_proc_hot = resp & GENMASK(7, 0); 468 + 469 + _get_uncore_p0_p1_info(id, config_index, ctdp_level); 470 + _get_p1_info(id, config_index, ctdp_level); 471 + _get_uncore_mem_freq(id, config_index, ctdp_level); 472 + 473 + debug_printf( 474 + "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n", 475 + id->cpu, config_index, resp, ctdp_level->t_proc_hot); 476 + 477 + return 0; 478 + } 479 + 480 + static int mbox_get_pwr_info(struct isst_id *id, int config_index, 481 + struct isst_pkg_ctdp_level_info *ctdp_level) 482 + { 483 + unsigned int resp; 484 + int ret; 485 + 486 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, 487 + 0, config_index, &resp); 488 + if (ret) 489 + return ret; 490 + 491 + ctdp_level->pkg_max_power = resp & GENMASK(14, 0); 492 + ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16; 493 + 494 + debug_printf( 495 + "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n", 496 + id->cpu, config_index, resp, ctdp_level->pkg_max_power, 497 + ctdp_level->pkg_min_power); 498 + 499 + return 0; 500 + } 501 + 502 + static int mbox_get_coremask_info(struct isst_id *id, int config_index, 503 + struct isst_pkg_ctdp_level_info *ctdp_level) 504 + { 505 + unsigned int resp; 506 + int i, ret; 507 + 508 + ctdp_level->cpu_count = 0; 509 + for (i = 0; i < 2; ++i) { 510 + unsigned long long mask; 511 + int cpu_count = 0; 512 + 513 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 514 + CONFIG_TDP_GET_CORE_MASK, 0, 515 + (i << 8) | config_index, &resp); 516 + if (ret) 517 + return ret; 518 + 519 + debug_printf( 520 + "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n", 521 + id->cpu, config_index, i, resp); 522 + 523 + mask = (unsigned long long)resp << (32 * i); 524 + set_cpu_mask_from_punit_coremask(id, mask, 525 + ctdp_level->core_cpumask_size, 526 + ctdp_level->core_cpumask, 527 + &cpu_count); 528 + ctdp_level->cpu_count += cpu_count; 529 + debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", id->cpu, 530 + config_index, i, ctdp_level->cpu_count); 531 + } 532 + 533 + return 0; 534 + } 535 + 536 + static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) 537 + { 538 + unsigned int req, resp; 539 + int ret; 540 + 541 + req = level | (avx_level << 16); 542 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 543 + CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, 544 + &resp); 545 + if (ret) 546 + return ret; 547 + 548 + debug_printf( 549 + "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n", 550 + id->cpu, req, resp); 551 + 552 + trl[0] = resp & GENMASK(7, 0); 553 + trl[1] = (resp & GENMASK(15, 8)) >> 8; 554 + trl[2] = (resp & GENMASK(23, 16)) >> 16; 555 + trl[3] = (resp & GENMASK(31, 24)) >> 24; 556 + 557 + req = level | BIT(8) | (avx_level << 16); 558 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 559 + CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, 560 + &resp); 561 + if (ret) 562 + return ret; 563 + 564 + debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", id->cpu, 565 + req, resp); 566 + 567 + trl[4] = resp & GENMASK(7, 0); 568 + trl[5] = (resp & GENMASK(15, 8)) >> 8; 569 + trl[6] = (resp & GENMASK(23, 16)) >> 16; 570 + trl[7] = (resp & GENMASK(31, 24)) >> 24; 571 + 572 + return 0; 573 + } 574 + 575 + static int mbox_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level) 576 + { 577 + int trl_max_levels = isst_get_trl_max_levels(); 578 + int i, ret; 579 + 580 + for (i = 0; i < trl_max_levels; i++) { 581 + ret = mbox_get_get_trl(id, level, i, ctdp_level->trl_ratios[i]); 582 + if (ret) 583 + return ret; 584 + } 585 + return 0; 586 + } 587 + 588 + static int mbox_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) 589 + { 590 + int ret; 591 + 592 + debug_printf("cpu:%d bucket info via MSR\n", id->cpu); 593 + 594 + *buckets_info = 0; 595 + 596 + ret = isst_send_msr_command(id->cpu, 0x1ae, 0, buckets_info); 597 + if (ret) 598 + return ret; 599 + 600 + debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", id->cpu, 601 + *buckets_info); 602 + 603 + return 0; 604 + } 605 + 606 + static int mbox_set_tdp_level(struct isst_id *id, int tdp_level) 607 + { 608 + unsigned int resp; 609 + int ret; 610 + 611 + 612 + if (isst_get_config_tdp_lock_status(id)) { 613 + isst_display_error_info_message(1, "TDP is locked", 0, 0); 614 + return -1; 615 + 616 + } 617 + 618 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, 619 + tdp_level, &resp); 620 + if (ret) { 621 + isst_display_error_info_message(1, "Set TDP level failed for level", 1, tdp_level); 622 + return ret; 623 + } 624 + 625 + return 0; 626 + } 627 + 628 + static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info) 629 + { 630 + int max_punit_core, max_mask_index; 631 + unsigned int req, resp; 632 + int i, ret; 633 + 634 + max_punit_core = get_max_punit_core_id(id); 635 + max_mask_index = max_punit_core > 32 ? 2 : 1; 636 + 637 + for (i = 0; i < max_mask_index; ++i) { 638 + unsigned long long mask; 639 + int count; 640 + 641 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 642 + CONFIG_TDP_PBF_GET_CORE_MASK_INFO, 643 + 0, (i << 8) | level, &resp); 644 + if (ret) 645 + break; 646 + 647 + debug_printf( 648 + "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n", 649 + id->cpu, resp); 650 + 651 + mask = (unsigned long long)resp << (32 * i); 652 + set_cpu_mask_from_punit_coremask(id, mask, 653 + pbf_info->core_cpumask_size, 654 + pbf_info->core_cpumask, 655 + &count); 656 + } 657 + 658 + req = level; 659 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 660 + CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req, 661 + &resp); 662 + if (ret) 663 + return ret; 664 + 665 + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", id->cpu, 666 + resp); 667 + 668 + pbf_info->p1_low = resp & 0xff; 669 + pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8; 670 + 671 + req = level; 672 + ret = _send_mbox_command( 673 + id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp); 674 + if (ret) 675 + return ret; 676 + 677 + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", id->cpu, resp); 678 + 679 + pbf_info->tdp = resp & 0xffff; 680 + 681 + req = level; 682 + ret = _send_mbox_command( 683 + id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp); 684 + if (ret) 685 + return ret; 686 + 687 + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", id->cpu, 688 + resp); 689 + pbf_info->t_control = (resp >> 8) & 0xff; 690 + pbf_info->t_prochot = resp & 0xff; 691 + 692 + return 0; 693 + } 694 + 695 + static int mbox_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) 696 + { 697 + struct isst_pkg_ctdp pkg_dev; 698 + struct isst_pkg_ctdp_level_info ctdp_level; 699 + int current_level; 700 + unsigned int req = 0, resp; 701 + int ret; 702 + 703 + ret = isst_get_ctdp_levels(id, &pkg_dev); 704 + if (ret) 705 + debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); 706 + 707 + current_level = pkg_dev.current_level; 708 + 709 + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); 710 + if (ret) 711 + return ret; 712 + 713 + if (pbf) { 714 + if (ctdp_level.fact_enabled) 715 + req = BIT(16); 716 + 717 + if (enable) 718 + req |= BIT(17); 719 + else 720 + req &= ~BIT(17); 721 + } else { 722 + 723 + if (enable && !ctdp_level.sst_cp_enabled) 724 + isst_display_error_info_message(0, "Make sure to execute before: core-power enable", 0, 0); 725 + 726 + if (ctdp_level.pbf_enabled) 727 + req = BIT(17); 728 + 729 + if (enable) 730 + req |= BIT(16); 731 + else 732 + req &= ~BIT(16); 733 + } 734 + 735 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 736 + CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp); 737 + if (ret) 738 + return ret; 739 + 740 + debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n", 741 + id->cpu, pbf, req); 742 + 743 + return 0; 744 + } 745 + 746 + static int _get_fact_bucket_info(struct isst_id *id, int level, 747 + struct isst_fact_bucket_info *bucket_info) 748 + { 749 + unsigned int resp; 750 + int i, k, ret; 751 + 752 + for (i = 0; i < 2; ++i) { 753 + int j; 754 + 755 + ret = _send_mbox_command( 756 + id->cpu, CONFIG_TDP, 757 + CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0, 758 + (i << 8) | level, &resp); 759 + if (ret) 760 + return ret; 761 + 762 + debug_printf( 763 + "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n", 764 + id->cpu, i, level, resp); 765 + 766 + for (j = 0; j < 4; ++j) { 767 + bucket_info[j + (i * 4)].hp_cores = 768 + (resp >> (j * 8)) & 0xff; 769 + } 770 + } 771 + 772 + for (k = 0; k < 3; ++k) { 773 + for (i = 0; i < 2; ++i) { 774 + int j; 775 + 776 + ret = _send_mbox_command( 777 + id->cpu, CONFIG_TDP, 778 + CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0, 779 + (k << 16) | (i << 8) | level, &resp); 780 + if (ret) 781 + return ret; 782 + 783 + debug_printf( 784 + "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n", 785 + id->cpu, i, level, k, resp); 786 + 787 + for (j = 0; j < 4; ++j) { 788 + bucket_info[j + (i * 4)].hp_ratios[k] = 789 + (resp >> (j * 8)) & 0xff; 790 + } 791 + } 792 + } 793 + 794 + return 0; 795 + } 796 + 797 + static int mbox_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info) 798 + { 799 + unsigned int resp; 800 + int j, ret, print; 801 + 802 + ret = _send_mbox_command(id->cpu, CONFIG_TDP, 803 + CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0, 804 + level, &resp); 805 + if (ret) 806 + return ret; 807 + 808 + debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n", 809 + id->cpu, resp); 810 + 811 + fact_info->lp_ratios[0] = resp & 0xff; 812 + fact_info->lp_ratios[1] = (resp >> 8) & 0xff; 813 + fact_info->lp_ratios[2] = (resp >> 16) & 0xff; 814 + 815 + ret = _get_fact_bucket_info(id, level, fact_info->bucket_info); 816 + if (ret) 817 + return ret; 818 + 819 + print = 0; 820 + for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { 821 + if (fact_bucket != 0xff && fact_bucket != j) 822 + continue; 823 + 824 + if (!fact_info->bucket_info[j].hp_cores) 825 + break; 826 + 827 + print = 1; 828 + } 829 + if (!print) { 830 + isst_display_error_info_message(1, "Invalid bucket", 0, 0); 831 + return -1; 832 + } 833 + 834 + return 0; 835 + } 836 + 837 + static int mbox_get_clos_information(struct isst_id *id, int *enable, int *type) 838 + { 839 + unsigned int resp; 840 + int ret; 841 + 842 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, 843 + &resp); 844 + if (ret) 845 + return ret; 846 + 847 + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); 848 + 849 + if (resp & BIT(1)) 850 + *enable = 1; 851 + else 852 + *enable = 0; 853 + 854 + if (resp & BIT(2)) 855 + *type = 1; 856 + else 857 + *type = 0; 858 + 859 + return 0; 860 + } 861 + 862 + static int _write_pm_config(struct isst_id *id, int cp_state) 863 + { 864 + unsigned int req, resp; 865 + int ret; 866 + 867 + if (cp_state) 868 + req = BIT(16); 869 + else 870 + req = 0; 871 + 872 + ret = _send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, 873 + &resp); 874 + if (ret) 875 + return ret; 876 + 877 + debug_printf("cpu:%d WRITE_PM_CONFIG resp:%x\n", id->cpu, resp); 878 + 879 + return 0; 880 + } 881 + 882 + static int mbox_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) 883 + { 884 + unsigned int req, resp; 885 + int ret; 886 + 887 + if (!enable_clos) { 888 + struct isst_pkg_ctdp pkg_dev; 889 + struct isst_pkg_ctdp_level_info ctdp_level; 890 + 891 + ret = isst_get_ctdp_levels(id, &pkg_dev); 892 + if (ret) { 893 + debug_printf("isst_get_ctdp_levels\n"); 894 + return ret; 895 + } 896 + 897 + ret = isst_get_ctdp_control(id, pkg_dev.current_level, 898 + &ctdp_level); 899 + if (ret) 900 + return ret; 901 + 902 + if (ctdp_level.fact_enabled) { 903 + isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0); 904 + return -EINVAL; 905 + } 906 + ret = _write_pm_config(id, 0); 907 + if (ret) 908 + isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); 909 + } else { 910 + ret = _write_pm_config(id, 1); 911 + if (ret) 912 + isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); 913 + } 914 + 915 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, 916 + &resp); 917 + if (ret) { 918 + isst_display_error_info_message(1, "CLOS_PM_QOS_CONFIG command failed", 0, 0); 919 + return ret; 920 + } 921 + 922 + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); 923 + 924 + req = resp; 925 + 926 + if (enable_clos) 927 + req = req | BIT(1); 928 + else 929 + req = req & ~BIT(1); 930 + 931 + if (priority_type > 1) 932 + isst_display_error_info_message(1, "Invalid priority type: Changing type to ordered", 0, 0); 933 + 934 + if (priority_type) 935 + req = req | BIT(2); 936 + else 937 + req = req & ~BIT(2); 938 + 939 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 940 + BIT(MBOX_CMD_WRITE_BIT), req, &resp); 941 + if (ret) 942 + return ret; 943 + 944 + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", id->cpu, 945 + priority_type, req); 946 + 947 + return 0; 948 + } 949 + 950 + static int mbox_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) 951 + { 952 + unsigned int resp; 953 + int ret; 954 + 955 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, 956 + &resp); 957 + if (ret) 958 + return ret; 959 + 960 + clos_config->epp = resp & 0x0f; 961 + clos_config->clos_prop_prio = (resp >> 4) & 0x0f; 962 + clos_config->clos_min = (resp >> 8) & 0xff; 963 + clos_config->clos_max = (resp >> 16) & 0xff; 964 + clos_config->clos_desired = (resp >> 24) & 0xff; 965 + 966 + return 0; 967 + } 968 + 969 + static int mbox_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) 970 + { 971 + unsigned int req, resp; 972 + unsigned int param; 973 + int ret; 974 + 975 + req = clos_config->epp & 0x0f; 976 + req |= (clos_config->clos_prop_prio & 0x0f) << 4; 977 + req |= (clos_config->clos_min & 0xff) << 8; 978 + req |= (clos_config->clos_max & 0xff) << 16; 979 + req |= (clos_config->clos_desired & 0xff) << 24; 980 + 981 + param = BIT(MBOX_CMD_WRITE_BIT) | clos; 982 + 983 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, 984 + &resp); 985 + if (ret) 986 + return ret; 987 + 988 + debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", id->cpu, param, req); 989 + 990 + return 0; 991 + } 992 + 993 + static int mbox_clos_get_assoc_status(struct isst_id *id, int *clos_id) 994 + { 995 + unsigned int resp; 996 + unsigned int param; 997 + int core_id, ret; 998 + 999 + core_id = find_phy_core_num(id->cpu); 1000 + param = core_id; 1001 + 1002 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, 1003 + &resp); 1004 + if (ret) 1005 + return ret; 1006 + 1007 + debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", id->cpu, param, 1008 + resp); 1009 + *clos_id = (resp >> 16) & 0x03; 1010 + 1011 + return 0; 1012 + } 1013 + 1014 + static int mbox_clos_associate(struct isst_id *id, int clos_id) 1015 + { 1016 + unsigned int req, resp; 1017 + unsigned int param; 1018 + int core_id, ret; 1019 + 1020 + req = (clos_id & 0x03) << 16; 1021 + core_id = find_phy_core_num(id->cpu); 1022 + param = BIT(MBOX_CMD_WRITE_BIT) | core_id; 1023 + 1024 + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 1025 + req, &resp); 1026 + if (ret) 1027 + return ret; 1028 + 1029 + debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", id->cpu, param, 1030 + req); 1031 + 1032 + return 0; 1033 + } 1034 + 1035 + static struct isst_platform_ops mbox_ops = { 1036 + .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, 1037 + .get_trl_max_levels = mbox_get_trl_max_levels, 1038 + .get_trl_level_name = mbox_get_trl_level_name, 1039 + .update_platform_param = mbox_update_platform_param, 1040 + .is_punit_valid = mbox_is_punit_valid, 1041 + .read_pm_config = mbox_read_pm_config, 1042 + .get_config_levels = mbox_get_config_levels, 1043 + .get_ctdp_control = mbox_get_ctdp_control, 1044 + .get_tdp_info = mbox_get_tdp_info, 1045 + .get_pwr_info = mbox_get_pwr_info, 1046 + .get_coremask_info = mbox_get_coremask_info, 1047 + .get_get_trl = mbox_get_get_trl, 1048 + .get_get_trls = mbox_get_get_trls, 1049 + .get_trl_bucket_info = mbox_get_trl_bucket_info, 1050 + .set_tdp_level = mbox_set_tdp_level, 1051 + .get_pbf_info = mbox_get_pbf_info, 1052 + .set_pbf_fact_status = mbox_set_pbf_fact_status, 1053 + .get_fact_info = mbox_get_fact_info, 1054 + .adjust_uncore_freq = mbox_adjust_uncore_freq, 1055 + .get_clos_information = mbox_get_clos_information, 1056 + .pm_qos_config = mbox_pm_qos_config, 1057 + .pm_get_clos = mbox_pm_get_clos, 1058 + .set_clos = mbox_set_clos, 1059 + .clos_get_assoc_status = mbox_clos_get_assoc_status, 1060 + .clos_associate = mbox_clos_associate, 1061 + }; 1062 + 1063 + struct isst_platform_ops *mbox_get_platform_ops(void) 1064 + { 1065 + return &mbox_ops; 1066 + }
+787
tools/power/x86/intel-speed-select/isst-core-tpmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Speed Select -- Enumerate and control features for TPMI Interface 4 + * Copyright (c) 2022 Intel Corporation. 5 + */ 6 + 7 + #include <linux/isst_if.h> 8 + #include "isst.h" 9 + 10 + int tpmi_process_ioctl(int ioctl_no, void *info) 11 + { 12 + const char *pathname = "/dev/isst_interface"; 13 + int fd; 14 + 15 + if (is_debug_enabled()) { 16 + debug_printf("Issue IOCTL: "); 17 + switch (ioctl_no) { 18 + case ISST_IF_CORE_POWER_STATE: 19 + debug_printf("ISST_IF_CORE_POWER_STATE\n"); 20 + break; 21 + case ISST_IF_CLOS_PARAM: 22 + debug_printf("ISST_IF_CLOS_PARAM\n"); 23 + break; 24 + case ISST_IF_CLOS_ASSOC: 25 + debug_printf("ISST_IF_CLOS_ASSOC\n"); 26 + break; 27 + case ISST_IF_PERF_LEVELS: 28 + debug_printf("ISST_IF_PERF_LEVELS\n"); 29 + break; 30 + case ISST_IF_PERF_SET_LEVEL: 31 + debug_printf("ISST_IF_PERF_SET_LEVEL\n"); 32 + break; 33 + case ISST_IF_PERF_SET_FEATURE: 34 + debug_printf("ISST_IF_PERF_SET_FEATURE\n"); 35 + break; 36 + case ISST_IF_GET_PERF_LEVEL_INFO: 37 + debug_printf("ISST_IF_GET_PERF_LEVEL_INFO\n"); 38 + break; 39 + case ISST_IF_GET_PERF_LEVEL_CPU_MASK: 40 + debug_printf("ISST_IF_GET_PERF_LEVEL_CPU_MASK\n"); 41 + break; 42 + case ISST_IF_GET_BASE_FREQ_INFO: 43 + debug_printf("ISST_IF_GET_BASE_FREQ_INFO\n"); 44 + break; 45 + case ISST_IF_GET_BASE_FREQ_CPU_MASK: 46 + debug_printf("ISST_IF_GET_BASE_FREQ_CPU_MASK\n"); 47 + break; 48 + case ISST_IF_GET_TURBO_FREQ_INFO: 49 + debug_printf("ISST_IF_GET_TURBO_FREQ_INFO\n"); 50 + break; 51 + case ISST_IF_COUNT_TPMI_INSTANCES: 52 + debug_printf("ISST_IF_COUNT_TPMI_INSTANCES\n"); 53 + break; 54 + default: 55 + debug_printf("%d\n", ioctl_no); 56 + break; 57 + } 58 + } 59 + 60 + fd = open(pathname, O_RDWR); 61 + if (fd < 0) 62 + return -1; 63 + 64 + if (ioctl(fd, ioctl_no, info) == -1) { 65 + debug_printf("IOCTL %d Failed\n", ioctl_no); 66 + close(fd); 67 + return -1; 68 + } 69 + 70 + close(fd); 71 + 72 + return 0; 73 + } 74 + 75 + static int tpmi_get_disp_freq_multiplier(void) 76 + { 77 + return 1; 78 + } 79 + 80 + static int tpmi_get_trl_max_levels(void) 81 + { 82 + return TRL_MAX_LEVELS; 83 + } 84 + 85 + static char *tpmi_get_trl_level_name(int level) 86 + { 87 + switch (level) { 88 + case 0: 89 + return "level-0"; 90 + case 1: 91 + return "level-1"; 92 + case 2: 93 + return "level-2"; 94 + case 3: 95 + return "level-3"; 96 + case 4: 97 + return "level-4"; 98 + case 5: 99 + return "level-5"; 100 + case 6: 101 + return "level-6"; 102 + case 7: 103 + return "level-7"; 104 + default: 105 + return NULL; 106 + } 107 + } 108 + 109 + 110 + static void tpmi_update_platform_param(enum isst_platform_param param, int value) 111 + { 112 + /* No params need to be updated for now */ 113 + } 114 + 115 + static int tpmi_is_punit_valid(struct isst_id *id) 116 + { 117 + struct isst_tpmi_instance_count info; 118 + int ret; 119 + 120 + if (id->punit < 0) 121 + return 0; 122 + 123 + info.socket_id = id->pkg; 124 + ret = tpmi_process_ioctl(ISST_IF_COUNT_TPMI_INSTANCES, &info); 125 + if (ret == -1) 126 + return 0; 127 + 128 + if (info.valid_mask & BIT(id->punit)) 129 + return 1; 130 + 131 + return 0; 132 + } 133 + 134 + static int tpmi_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) 135 + { 136 + struct isst_core_power info; 137 + int ret; 138 + 139 + info.get_set = 0; 140 + info.socket_id = id->pkg; 141 + info.power_domain_id = id->punit; 142 + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); 143 + if (ret == -1) 144 + return ret; 145 + 146 + *cp_state = info.enable; 147 + *cp_cap = info.supported; 148 + 149 + return 0; 150 + } 151 + 152 + int tpmi_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) 153 + { 154 + struct isst_perf_level_info info; 155 + int ret; 156 + 157 + info.socket_id = id->pkg; 158 + info.power_domain_id = id->punit; 159 + 160 + ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info); 161 + if (ret == -1) 162 + return ret; 163 + 164 + pkg_dev->version = info.feature_rev; 165 + pkg_dev->levels = info.max_level; 166 + pkg_dev->locked = info.locked; 167 + pkg_dev->current_level = info.current_level; 168 + pkg_dev->locked = info.locked; 169 + pkg_dev->enabled = info.enabled; 170 + 171 + return 0; 172 + } 173 + 174 + static int tpmi_get_ctdp_control(struct isst_id *id, int config_index, 175 + struct isst_pkg_ctdp_level_info *ctdp_level) 176 + { 177 + struct isst_core_power core_power_info; 178 + struct isst_perf_level_info info; 179 + int level_mask; 180 + int ret; 181 + 182 + info.socket_id = id->pkg; 183 + info.power_domain_id = id->punit; 184 + 185 + ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info); 186 + if (ret == -1) 187 + return -1; 188 + 189 + if (config_index != 0xff) 190 + level_mask = 1 << config_index; 191 + else 192 + level_mask = config_index; 193 + 194 + if (!(info.level_mask & level_mask)) 195 + return -1; 196 + 197 + ctdp_level->fact_support = info.sst_tf_support; 198 + ctdp_level->pbf_support = info.sst_bf_support; 199 + ctdp_level->fact_enabled = !!(info.feature_state & BIT(1)); 200 + ctdp_level->pbf_enabled = !!(info.feature_state & BIT(0)); 201 + 202 + core_power_info.get_set = 0; 203 + core_power_info.socket_id = id->pkg; 204 + core_power_info.power_domain_id = id->punit; 205 + 206 + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &core_power_info); 207 + if (ret == -1) 208 + return ret; 209 + 210 + ctdp_level->sst_cp_support = core_power_info.supported; 211 + ctdp_level->sst_cp_enabled = core_power_info.enable; 212 + 213 + debug_printf 214 + ("cpu:%d CONFIG_TDP_GET_TDP_CONTROL fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", 215 + id->cpu, ctdp_level->fact_support, ctdp_level->pbf_support, 216 + ctdp_level->fact_enabled, ctdp_level->pbf_enabled); 217 + 218 + return 0; 219 + } 220 + 221 + static int tpmi_get_tdp_info(struct isst_id *id, int config_index, 222 + struct isst_pkg_ctdp_level_info *ctdp_level) 223 + { 224 + struct isst_perf_level_data_info info; 225 + int ret; 226 + 227 + info.socket_id = id->pkg; 228 + info.power_domain_id = id->punit; 229 + info.level = config_index; 230 + 231 + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); 232 + if (ret == -1) 233 + return ret; 234 + 235 + ctdp_level->pkg_tdp = info.thermal_design_power_w; 236 + ctdp_level->tdp_ratio = info.tdp_ratio; 237 + ctdp_level->sse_p1 = info.base_freq_mhz; 238 + ctdp_level->avx2_p1 = info.base_freq_avx2_mhz; 239 + ctdp_level->avx512_p1 = info.base_freq_avx512_mhz; 240 + ctdp_level->amx_p1 = info.base_freq_amx_mhz; 241 + 242 + ctdp_level->t_proc_hot = info.tjunction_max_c; 243 + ctdp_level->mem_freq = info.max_memory_freq_mhz; 244 + ctdp_level->cooling_type = info.cooling_type; 245 + 246 + ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz; 247 + ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz; 248 + ctdp_level->uncore_pm = info.pm_fabric_freq_mhz; 249 + 250 + debug_printf 251 + ("cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO tdp_ratio:%d pkg_tdp:%d ctdp_level->t_proc_hot:%d\n", 252 + id->cpu, config_index, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp, 253 + ctdp_level->t_proc_hot); 254 + 255 + return 0; 256 + } 257 + 258 + static int tpmi_get_pwr_info(struct isst_id *id, int config_index, 259 + struct isst_pkg_ctdp_level_info *ctdp_level) 260 + { 261 + /* TBD */ 262 + ctdp_level->pkg_max_power = 0; 263 + ctdp_level->pkg_min_power = 0; 264 + 265 + debug_printf 266 + ("cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO pkg_max_power:%d pkg_min_power:%d\n", 267 + id->cpu, config_index, ctdp_level->pkg_max_power, 268 + ctdp_level->pkg_min_power); 269 + 270 + return 0; 271 + } 272 + 273 + int tpmi_get_coremask_info(struct isst_id *id, int config_index, 274 + struct isst_pkg_ctdp_level_info *ctdp_level) 275 + { 276 + struct isst_perf_level_cpu_mask info; 277 + int ret, cpu_count; 278 + 279 + info.socket_id = id->pkg; 280 + info.power_domain_id = id->punit; 281 + info.level = config_index; 282 + info.punit_cpu_map = 1; 283 + 284 + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_CPU_MASK, &info); 285 + if (ret == -1) 286 + return ret; 287 + 288 + set_cpu_mask_from_punit_coremask(id, info.mask, 289 + ctdp_level->core_cpumask_size, 290 + ctdp_level->core_cpumask, &cpu_count); 291 + ctdp_level->cpu_count = cpu_count; 292 + 293 + debug_printf("cpu:%d ctdp:%d core_mask ino cpu count:%d\n", 294 + id->cpu, config_index, ctdp_level->cpu_count); 295 + 296 + return 0; 297 + } 298 + 299 + static int tpmi_get_get_trls(struct isst_id *id, int config_index, 300 + struct isst_pkg_ctdp_level_info *ctdp_level) 301 + { 302 + struct isst_perf_level_data_info info; 303 + int ret, i, j; 304 + 305 + info.socket_id = id->pkg; 306 + info.power_domain_id = id->punit; 307 + info.level = config_index; 308 + 309 + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); 310 + if (ret == -1) 311 + return ret; 312 + 313 + if (info.max_buckets > TRL_MAX_BUCKETS) 314 + info.max_buckets = TRL_MAX_BUCKETS; 315 + 316 + if (info.max_trl_levels > TRL_MAX_LEVELS) 317 + info.max_trl_levels = TRL_MAX_LEVELS; 318 + 319 + for (i = 0; i < info.max_trl_levels; ++i) 320 + for (j = 0; j < info.max_buckets; ++j) 321 + ctdp_level->trl_ratios[i][j] = info.trl_freq_mhz[i][j]; 322 + 323 + return 0; 324 + } 325 + 326 + static int tpmi_get_get_trl(struct isst_id *id, int level, int config_index, 327 + int *trl) 328 + { 329 + struct isst_pkg_ctdp_level_info ctdp_level; 330 + int ret, i; 331 + 332 + ret = tpmi_get_get_trls(id, config_index, &ctdp_level); 333 + if (ret) 334 + return ret; 335 + 336 + /* FIX ME: Just return for level 0 */ 337 + for (i = 0; i < 8; ++i) 338 + trl[i] = ctdp_level.trl_ratios[0][i]; 339 + 340 + return 0; 341 + } 342 + 343 + static int tpmi_get_trl_bucket_info(struct isst_id *id, int config_index, 344 + unsigned long long *buckets_info) 345 + { 346 + struct isst_perf_level_data_info info; 347 + unsigned char *mask = (unsigned char *)buckets_info; 348 + int ret, i; 349 + 350 + info.socket_id = id->pkg; 351 + info.power_domain_id = id->punit; 352 + info.level = config_index; 353 + 354 + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); 355 + if (ret == -1) 356 + return ret; 357 + 358 + if (info.max_buckets > TRL_MAX_BUCKETS) 359 + info.max_buckets = TRL_MAX_BUCKETS; 360 + 361 + for (i = 0; i < info.max_buckets; ++i) 362 + mask[i] = info.bucket_core_counts[i]; 363 + 364 + debug_printf("cpu:%d TRL bucket info: 0x%llx\n", id->cpu, 365 + *buckets_info); 366 + 367 + return 0; 368 + } 369 + 370 + static int tpmi_set_tdp_level(struct isst_id *id, int tdp_level) 371 + { 372 + struct isst_perf_level_control info; 373 + int ret; 374 + 375 + info.socket_id = id->pkg; 376 + info.power_domain_id = id->punit; 377 + info.level = tdp_level; 378 + 379 + ret = tpmi_process_ioctl(ISST_IF_PERF_SET_LEVEL, &info); 380 + if (ret == -1) 381 + return ret; 382 + 383 + return 0; 384 + } 385 + 386 + static int _pbf_get_coremask_info(struct isst_id *id, int config_index, 387 + struct isst_pbf_info *pbf_info) 388 + { 389 + struct isst_perf_level_cpu_mask info; 390 + int ret, cpu_count; 391 + 392 + info.socket_id = id->pkg; 393 + info.power_domain_id = id->punit; 394 + info.level = config_index; 395 + info.punit_cpu_map = 1; 396 + 397 + ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_CPU_MASK, &info); 398 + if (ret == -1) 399 + return ret; 400 + 401 + set_cpu_mask_from_punit_coremask(id, info.mask, 402 + pbf_info->core_cpumask_size, 403 + pbf_info->core_cpumask, &cpu_count); 404 + 405 + debug_printf("cpu:%d ctdp:%d pbf core_mask info cpu count:%d\n", 406 + id->cpu, config_index, cpu_count); 407 + 408 + return 0; 409 + } 410 + 411 + static int tpmi_get_pbf_info(struct isst_id *id, int level, 412 + struct isst_pbf_info *pbf_info) 413 + { 414 + struct isst_base_freq_info info; 415 + int ret; 416 + 417 + info.socket_id = id->pkg; 418 + info.power_domain_id = id->punit; 419 + info.level = level; 420 + 421 + ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_INFO, &info); 422 + if (ret == -1) 423 + return ret; 424 + 425 + pbf_info->p1_low = info.low_base_freq_mhz; 426 + pbf_info->p1_high = info.high_base_freq_mhz; 427 + pbf_info->tdp = info.thermal_design_power_w; 428 + pbf_info->t_prochot = info.tjunction_max_c; 429 + 430 + debug_printf("cpu:%d ctdp:%d pbf info:%d:%d:%d:%d\n", 431 + id->cpu, level, pbf_info->p1_low, pbf_info->p1_high, 432 + pbf_info->tdp, pbf_info->t_prochot); 433 + 434 + return _pbf_get_coremask_info(id, level, pbf_info); 435 + } 436 + 437 + static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) 438 + { 439 + struct isst_pkg_ctdp pkg_dev; 440 + struct isst_pkg_ctdp_level_info ctdp_level; 441 + int current_level; 442 + struct isst_perf_feature_control info; 443 + int ret; 444 + 445 + ret = isst_get_ctdp_levels(id, &pkg_dev); 446 + if (ret) 447 + debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); 448 + 449 + current_level = pkg_dev.current_level; 450 + 451 + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); 452 + if (ret) 453 + return ret; 454 + 455 + info.socket_id = id->pkg; 456 + info.power_domain_id = id->punit; 457 + 458 + info.feature = 0; 459 + 460 + if (pbf) { 461 + if (ctdp_level.fact_enabled) 462 + info.feature |= BIT(1); 463 + 464 + if (enable) 465 + info.feature |= BIT(0); 466 + else 467 + info.feature &= ~BIT(0); 468 + } else { 469 + 470 + if (enable && !ctdp_level.sst_cp_enabled) 471 + isst_display_error_info_message(0, 472 + "Make sure to execute before: core-power enable", 473 + 0, 0); 474 + 475 + if (ctdp_level.pbf_enabled) 476 + info.feature |= BIT(0); 477 + 478 + if (enable) 479 + info.feature |= BIT(1); 480 + else 481 + info.feature &= ~BIT(1); 482 + } 483 + 484 + ret = tpmi_process_ioctl(ISST_IF_PERF_SET_FEATURE, &info); 485 + if (ret == -1) 486 + return ret; 487 + 488 + return 0; 489 + } 490 + 491 + static int tpmi_get_fact_info(struct isst_id *id, int level, int fact_bucket, 492 + struct isst_fact_info *fact_info) 493 + { 494 + struct isst_turbo_freq_info info; 495 + int i, j; 496 + int ret; 497 + 498 + info.socket_id = id->pkg; 499 + info.power_domain_id = id->punit; 500 + info.level = level; 501 + 502 + ret = tpmi_process_ioctl(ISST_IF_GET_TURBO_FREQ_INFO, &info); 503 + if (ret == -1) 504 + return ret; 505 + 506 + for (i = 0; i < info.max_clip_freqs; ++i) 507 + fact_info->lp_ratios[i] = info.lp_clip_freq_mhz[i]; 508 + 509 + if (info.max_buckets > TRL_MAX_BUCKETS) 510 + info.max_buckets = TRL_MAX_BUCKETS; 511 + 512 + if (info.max_trl_levels > TRL_MAX_LEVELS) 513 + info.max_trl_levels = TRL_MAX_LEVELS; 514 + 515 + for (i = 0; i < info.max_trl_levels; ++i) { 516 + for (j = 0; j < info.max_buckets; ++j) 517 + fact_info->bucket_info[j].hp_ratios[i] = 518 + info.trl_freq_mhz[i][j]; 519 + } 520 + 521 + for (i = 0; i < info.max_buckets; ++i) 522 + fact_info->bucket_info[i].hp_cores = info.bucket_core_counts[i]; 523 + 524 + return 0; 525 + } 526 + 527 + static void _set_uncore_min_max(struct isst_id *id, int max, int freq) 528 + { 529 + DIR *dir; 530 + FILE *filep; 531 + struct dirent *entry; 532 + char buffer[512]; 533 + unsigned int tmp_id; 534 + int ret; 535 + 536 + dir = opendir("/sys/devices/system/cpu/intel_uncore_frequency/"); 537 + if (!dir) 538 + return; 539 + 540 + while ((entry = readdir(dir)) != NULL ) { 541 + /* Check domain_id */ 542 + snprintf(buffer, sizeof(buffer), 543 + "/sys/devices/system/cpu/intel_uncore_frequency/%s/domain_id", entry->d_name); 544 + 545 + filep = fopen(buffer, "r"); 546 + if (!filep) 547 + goto end; 548 + 549 + ret = fscanf(filep, "%u", &tmp_id); 550 + fclose(filep); 551 + if (ret != 1) 552 + goto end; 553 + 554 + if (tmp_id != id->punit) 555 + continue; 556 + 557 + /* Check package_id */ 558 + snprintf(buffer, sizeof(buffer), 559 + "/sys/devices/system/cpu/intel_uncore_frequency/%s/package_id", entry->d_name); 560 + 561 + filep = fopen(buffer, "r"); 562 + if (!filep) 563 + goto end; 564 + 565 + ret = fscanf(filep, "%u", &tmp_id); 566 + fclose(filep); 567 + 568 + if (ret != 1) 569 + goto end; 570 + 571 + if (tmp_id != id->pkg) 572 + continue; 573 + 574 + /* Found the right sysfs path, adjust and quit */ 575 + if (max) 576 + snprintf(buffer, sizeof(buffer), 577 + "/sys/devices/system/cpu/intel_uncore_frequency/%s/max_freq_khz", entry->d_name); 578 + else 579 + snprintf(buffer, sizeof(buffer), 580 + "/sys/devices/system/cpu/intel_uncore_frequency/%s/min_freq_khz", entry->d_name); 581 + 582 + filep = fopen(buffer, "w"); 583 + if (!filep) 584 + goto end; 585 + 586 + fprintf(filep, "%d\n", freq); 587 + fclose(filep); 588 + break; 589 + } 590 + 591 + end: 592 + closedir(dir); 593 + } 594 + 595 + static void tpmi_adjust_uncore_freq(struct isst_id *id, int config_index, 596 + struct isst_pkg_ctdp_level_info *ctdp_level) 597 + { 598 + struct isst_perf_level_data_info info; 599 + int ret; 600 + 601 + info.socket_id = id->pkg; 602 + info.power_domain_id = id->punit; 603 + info.level = config_index; 604 + 605 + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); 606 + if (ret == -1) 607 + return; 608 + 609 + ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz; 610 + ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz; 611 + ctdp_level->uncore_pm = info.pm_fabric_freq_mhz; 612 + 613 + if (ctdp_level->uncore_pm) 614 + _set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000); 615 + 616 + if (ctdp_level->uncore_p0) 617 + _set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000); 618 + 619 + return; 620 + } 621 + 622 + static int tpmi_get_clos_information(struct isst_id *id, int *enable, int *type) 623 + { 624 + struct isst_core_power info; 625 + int ret; 626 + 627 + info.get_set = 0; 628 + info.socket_id = id->pkg; 629 + info.power_domain_id = id->punit; 630 + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); 631 + if (ret == -1) 632 + return ret; 633 + 634 + *enable = info.enable; 635 + *type = info.priority_type; 636 + 637 + return 0; 638 + } 639 + 640 + static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos, 641 + int priority_type) 642 + { 643 + struct isst_core_power info; 644 + int ret; 645 + 646 + info.get_set = 1; 647 + info.socket_id = id->pkg; 648 + info.power_domain_id = id->punit; 649 + info.enable = enable_clos; 650 + info.priority_type = priority_type; 651 + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); 652 + if (ret == -1) 653 + return ret; 654 + 655 + return 0; 656 + } 657 + 658 + int tpmi_pm_get_clos(struct isst_id *id, int clos, 659 + struct isst_clos_config *clos_config) 660 + { 661 + struct isst_clos_param info; 662 + int ret; 663 + 664 + info.get_set = 0; 665 + info.socket_id = id->pkg; 666 + info.power_domain_id = id->punit; 667 + info.clos = clos; 668 + 669 + ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info); 670 + if (ret == -1) 671 + return ret; 672 + 673 + clos_config->epp = 0; 674 + clos_config->clos_prop_prio = info.prop_prio; 675 + clos_config->clos_min = info.min_freq_mhz; 676 + clos_config->clos_max = info.max_freq_mhz; 677 + clos_config->clos_desired = 0; 678 + 679 + debug_printf("cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos, 680 + clos_config->clos_min, clos_config->clos_max); 681 + 682 + return 0; 683 + } 684 + 685 + int tpmi_set_clos(struct isst_id *id, int clos, 686 + struct isst_clos_config *clos_config) 687 + { 688 + struct isst_clos_param info; 689 + int ret; 690 + 691 + info.get_set = 1; 692 + info.socket_id = id->pkg; 693 + info.power_domain_id = id->punit; 694 + info.clos = clos; 695 + info.prop_prio = clos_config->clos_prop_prio; 696 + 697 + info.min_freq_mhz = clos_config->clos_min; 698 + info.max_freq_mhz = clos_config->clos_max; 699 + 700 + if (info.min_freq_mhz <= 0xff) 701 + info.min_freq_mhz *= 100; 702 + if (info.max_freq_mhz <= 0xff) 703 + info.max_freq_mhz *= 100; 704 + 705 + ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info); 706 + if (ret == -1) 707 + return ret; 708 + 709 + debug_printf("set cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos, 710 + clos_config->clos_min, clos_config->clos_max); 711 + 712 + return 0; 713 + } 714 + 715 + static int tpmi_clos_get_assoc_status(struct isst_id *id, int *clos_id) 716 + { 717 + struct isst_if_clos_assoc_cmds assoc_cmds; 718 + int ret; 719 + 720 + assoc_cmds.cmd_count = 1; 721 + assoc_cmds.get_set = 0; 722 + assoc_cmds.punit_cpu_map = 1; 723 + assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu); 724 + assoc_cmds.assoc_info[0].socket_id = id->pkg; 725 + assoc_cmds.assoc_info[0].power_domain_id = id->punit; 726 + 727 + ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds); 728 + if (ret == -1) 729 + return ret; 730 + 731 + *clos_id = assoc_cmds.assoc_info[0].clos; 732 + 733 + return 0; 734 + } 735 + 736 + static int tpmi_clos_associate(struct isst_id *id, int clos_id) 737 + { 738 + struct isst_if_clos_assoc_cmds assoc_cmds; 739 + int ret; 740 + 741 + assoc_cmds.cmd_count = 1; 742 + assoc_cmds.get_set = 1; 743 + assoc_cmds.punit_cpu_map = 1; 744 + assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu); 745 + assoc_cmds.assoc_info[0].clos = clos_id; 746 + assoc_cmds.assoc_info[0].socket_id = id->pkg; 747 + assoc_cmds.assoc_info[0].power_domain_id = id->punit; 748 + 749 + ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds); 750 + if (ret == -1) 751 + return ret; 752 + 753 + return 0; 754 + } 755 + 756 + static struct isst_platform_ops tpmi_ops = { 757 + .get_disp_freq_multiplier = tpmi_get_disp_freq_multiplier, 758 + .get_trl_max_levels = tpmi_get_trl_max_levels, 759 + .get_trl_level_name = tpmi_get_trl_level_name, 760 + .update_platform_param = tpmi_update_platform_param, 761 + .is_punit_valid = tpmi_is_punit_valid, 762 + .read_pm_config = tpmi_read_pm_config, 763 + .get_config_levels = tpmi_get_config_levels, 764 + .get_ctdp_control = tpmi_get_ctdp_control, 765 + .get_tdp_info = tpmi_get_tdp_info, 766 + .get_pwr_info = tpmi_get_pwr_info, 767 + .get_coremask_info = tpmi_get_coremask_info, 768 + .get_get_trl = tpmi_get_get_trl, 769 + .get_get_trls = tpmi_get_get_trls, 770 + .get_trl_bucket_info = tpmi_get_trl_bucket_info, 771 + .set_tdp_level = tpmi_set_tdp_level, 772 + .get_pbf_info = tpmi_get_pbf_info, 773 + .set_pbf_fact_status = tpmi_set_pbf_fact_status, 774 + .get_fact_info = tpmi_get_fact_info, 775 + .adjust_uncore_freq = tpmi_adjust_uncore_freq, 776 + .get_clos_information = tpmi_get_clos_information, 777 + .pm_qos_config = tpmi_pm_qos_config, 778 + .pm_get_clos = tpmi_pm_get_clos, 779 + .set_clos = tpmi_set_clos, 780 + .clos_get_assoc_status = tpmi_clos_get_assoc_status, 781 + .clos_associate = tpmi_clos_associate, 782 + }; 783 + 784 + struct isst_platform_ops *tpmi_get_platform_ops(void) 785 + { 786 + return &tpmi_ops; 787 + }
+147 -719
tools/power/x86/intel-speed-select/isst-core.c
··· 6 6 7 7 #include "isst.h" 8 8 9 - int isst_write_pm_config(struct isst_id *id, int cp_state) 9 + static struct isst_platform_ops *isst_ops; 10 + 11 + #define CHECK_CB(_name) \ 12 + do { \ 13 + if (!isst_ops || !isst_ops->_name) { \ 14 + fprintf(stderr, "Invalid ops\n"); \ 15 + exit(0); \ 16 + } \ 17 + } while (0) 18 + 19 + int isst_set_platform_ops(int api_version) 10 20 { 11 - unsigned int req, resp; 12 - int ret; 21 + switch (api_version) { 22 + case 1: 23 + isst_ops = mbox_get_platform_ops(); 24 + break; 25 + case 2: 26 + isst_ops = tpmi_get_platform_ops(); 27 + break; 28 + default: 29 + isst_ops = NULL; 30 + break; 31 + } 13 32 14 - if (cp_state) 15 - req = BIT(16); 16 - else 17 - req = 0; 33 + if (!isst_ops) 34 + return -1; 35 + return 0; 36 + } 18 37 19 - ret = isst_send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, 20 - &resp); 21 - if (ret) 22 - return ret; 38 + void isst_update_platform_param(enum isst_platform_param param, int value) 39 + { 40 + CHECK_CB(update_platform_param); 23 41 24 - debug_printf("cpu:%d WRITE_PM_CONFIG resp:%x\n", id->cpu, resp); 42 + isst_ops->update_platform_param(param, value); 43 + } 44 + 45 + int isst_get_disp_freq_multiplier(void) 46 + { 47 + CHECK_CB(get_disp_freq_multiplier); 48 + return isst_ops->get_disp_freq_multiplier(); 49 + } 50 + 51 + int isst_get_trl_max_levels(void) 52 + { 53 + CHECK_CB(get_trl_max_levels); 54 + return isst_ops->get_trl_max_levels(); 55 + } 56 + 57 + char *isst_get_trl_level_name(int level) 58 + { 59 + CHECK_CB(get_trl_level_name); 60 + return isst_ops->get_trl_level_name(level); 61 + } 62 + 63 + int isst_is_punit_valid(struct isst_id *id) 64 + { 65 + CHECK_CB(is_punit_valid); 66 + return isst_ops->is_punit_valid(id); 67 + } 68 + 69 + int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, 70 + unsigned long long *req_resp) 71 + { 72 + struct isst_if_msr_cmds msr_cmds; 73 + const char *pathname = "/dev/isst_interface"; 74 + FILE *outf = get_output_file(); 75 + int fd; 76 + 77 + fd = open(pathname, O_RDWR); 78 + if (fd < 0) 79 + err(-1, "%s open failed", pathname); 80 + 81 + msr_cmds.cmd_count = 1; 82 + msr_cmds.msr_cmd[0].logical_cpu = cpu; 83 + msr_cmds.msr_cmd[0].msr = msr; 84 + msr_cmds.msr_cmd[0].read_write = write; 85 + if (write) 86 + msr_cmds.msr_cmd[0].data = *req_resp; 87 + 88 + if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) { 89 + perror("ISST_IF_MSR_COMMAND"); 90 + fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n", 91 + cpu, msr, write); 92 + } else { 93 + if (!write) 94 + *req_resp = msr_cmds.msr_cmd[0].data; 95 + 96 + debug_printf( 97 + "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n", 98 + cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data); 99 + } 100 + 101 + close(fd); 25 102 26 103 return 0; 27 104 } 28 105 29 106 int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) 30 107 { 31 - unsigned int resp; 32 - int ret; 33 - 34 - ret = isst_send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, 35 - &resp); 36 - if (ret) 37 - return ret; 38 - 39 - debug_printf("cpu:%d READ_PM_CONFIG resp:%x\n", id->cpu, resp); 40 - 41 - *cp_state = resp & BIT(16); 42 - *cp_cap = resp & BIT(0) ? 1 : 0; 43 - 44 - return 0; 108 + CHECK_CB(read_pm_config); 109 + return isst_ops->read_pm_config(id, cp_state, cp_cap); 45 110 } 46 111 47 112 int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) 48 113 { 49 - unsigned int resp; 50 - int ret; 51 - 52 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 53 - CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); 54 - if (ret) { 55 - pkg_dev->levels = 0; 56 - pkg_dev->locked = 1; 57 - pkg_dev->current_level = 0; 58 - pkg_dev->version = 0; 59 - pkg_dev->enabled = 0; 60 - return 0; 61 - } 62 - 63 - debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", id->cpu, resp); 64 - 65 - pkg_dev->version = resp & 0xff; 66 - pkg_dev->levels = (resp >> 8) & 0xff; 67 - pkg_dev->current_level = (resp >> 16) & 0xff; 68 - pkg_dev->locked = !!(resp & BIT(24)); 69 - pkg_dev->enabled = !!(resp & BIT(31)); 70 - 71 - return 0; 114 + CHECK_CB(get_config_levels); 115 + return isst_ops->get_config_levels(id, pkg_dev); 72 116 } 73 117 74 118 int isst_get_ctdp_control(struct isst_id *id, int config_index, 75 119 struct isst_pkg_ctdp_level_info *ctdp_level) 76 120 { 77 - int cp_state, cp_cap; 78 - unsigned int resp; 79 - int ret; 80 - 81 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 82 - CONFIG_TDP_GET_TDP_CONTROL, 0, 83 - config_index, &resp); 84 - if (ret) 85 - return ret; 86 - 87 - ctdp_level->fact_support = resp & BIT(0); 88 - ctdp_level->pbf_support = !!(resp & BIT(1)); 89 - ctdp_level->fact_enabled = !!(resp & BIT(16)); 90 - ctdp_level->pbf_enabled = !!(resp & BIT(17)); 91 - 92 - ret = isst_read_pm_config(id, &cp_state, &cp_cap); 93 - if (ret) { 94 - debug_printf("cpu:%d pm_config is not supported\n", id->cpu); 95 - } else { 96 - debug_printf("cpu:%d pm_config SST-CP state:%d cap:%d\n", id->cpu, cp_state, cp_cap); 97 - ctdp_level->sst_cp_support = cp_cap; 98 - ctdp_level->sst_cp_enabled = cp_state; 99 - } 100 - 101 - debug_printf( 102 - "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", 103 - id->cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support, 104 - ctdp_level->fact_enabled, ctdp_level->pbf_enabled); 105 - 106 - return 0; 121 + CHECK_CB(get_ctdp_control); 122 + return isst_ops->get_ctdp_control(id, config_index, ctdp_level); 107 123 } 108 124 109 125 int isst_get_tdp_info(struct isst_id *id, int config_index, 110 126 struct isst_pkg_ctdp_level_info *ctdp_level) 111 127 { 112 - unsigned int resp; 113 - int ret; 114 - 115 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, 116 - 0, config_index, &resp); 117 - if (ret) { 118 - isst_display_error_info_message(1, "Invalid level, Can't get TDP information at level", 1, config_index); 119 - return ret; 120 - } 121 - 122 - ctdp_level->pkg_tdp = resp & GENMASK(14, 0); 123 - ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16; 124 - 125 - debug_printf( 126 - "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n", 127 - id->cpu, config_index, resp, ctdp_level->tdp_ratio, 128 - ctdp_level->pkg_tdp); 129 - return 0; 128 + CHECK_CB(get_tdp_info); 129 + return isst_ops->get_tdp_info(id, config_index, ctdp_level); 130 130 } 131 131 132 132 int isst_get_pwr_info(struct isst_id *id, int config_index, 133 133 struct isst_pkg_ctdp_level_info *ctdp_level) 134 134 { 135 - unsigned int resp; 136 - int ret; 137 - 138 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, 139 - 0, config_index, &resp); 140 - if (ret) 141 - return ret; 142 - 143 - ctdp_level->pkg_max_power = resp & GENMASK(14, 0); 144 - ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16; 145 - 146 - debug_printf( 147 - "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n", 148 - id->cpu, config_index, resp, ctdp_level->pkg_max_power, 149 - ctdp_level->pkg_min_power); 150 - 151 - return 0; 152 - } 153 - 154 - void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, 155 - struct isst_pkg_ctdp_level_info *ctdp_level) 156 - { 157 - unsigned int resp; 158 - int ret; 159 - 160 - ctdp_level->uncore_pm = 0; 161 - ctdp_level->uncore_p0 = 0; 162 - ctdp_level->uncore_p1 = 0; 163 - 164 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 165 - CONFIG_TDP_GET_RATIO_INFO, 0, 166 - (BIT(16) | config_index), &resp); 167 - if (ret) 168 - goto try_uncore_mbox; 169 - 170 - ctdp_level->uncore_p0 = resp & GENMASK(7, 0); 171 - ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; 172 - ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; 173 - 174 - debug_printf( 175 - "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", 176 - id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, 177 - ctdp_level->uncore_pm); 178 - 179 - return; 180 - 181 - try_uncore_mbox: 182 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 183 - CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, 184 - config_index, &resp); 185 - if (ret) { 186 - ctdp_level->uncore_p0 = 0; 187 - ctdp_level->uncore_p1 = 0; 188 - return; 189 - } 190 - 191 - ctdp_level->uncore_p0 = resp & GENMASK(7, 0); 192 - ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; 193 - debug_printf( 194 - "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", 195 - id->cpu, config_index, resp, ctdp_level->uncore_p0, 196 - ctdp_level->uncore_p1); 197 - } 198 - 199 - void isst_get_p1_info(struct isst_id *id, int config_index, 200 - struct isst_pkg_ctdp_level_info *ctdp_level) 201 - { 202 - unsigned int resp; 203 - int ret; 204 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, 205 - config_index, &resp); 206 - if (ret) { 207 - ctdp_level->sse_p1 = 0; 208 - ctdp_level->avx2_p1 = 0; 209 - ctdp_level->avx512_p1 = 0; 210 - return; 211 - } 212 - 213 - ctdp_level->sse_p1 = resp & GENMASK(7, 0); 214 - ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; 215 - ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; 216 - debug_printf( 217 - "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", 218 - id->cpu, config_index, resp, ctdp_level->sse_p1, 219 - ctdp_level->avx2_p1, ctdp_level->avx512_p1); 220 - } 221 - 222 - void isst_get_uncore_mem_freq(struct isst_id *id, int config_index, 223 - struct isst_pkg_ctdp_level_info *ctdp_level) 224 - { 225 - unsigned int resp; 226 - int ret; 227 - 228 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, 229 - 0, config_index, &resp); 230 - if (ret) { 231 - ctdp_level->mem_freq = 0; 232 - return; 233 - } 234 - 235 - ctdp_level->mem_freq = resp & GENMASK(7, 0); 236 - if (is_spr_platform()) { 237 - ctdp_level->mem_freq *= 200; 238 - } else if (is_icx_platform()) { 239 - if (ctdp_level->mem_freq < 7) { 240 - ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; 241 - ctdp_level->mem_freq /= 10; 242 - if (ctdp_level->mem_freq % 10 > 5) 243 - ctdp_level->mem_freq++; 244 - } else { 245 - ctdp_level->mem_freq = 0; 246 - } 247 - } else { 248 - ctdp_level->mem_freq = 0; 249 - } 250 - debug_printf( 251 - "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", 252 - id->cpu, config_index, resp, ctdp_level->mem_freq); 253 - } 254 - 255 - int isst_get_tjmax_info(struct isst_id *id, int config_index, 256 - struct isst_pkg_ctdp_level_info *ctdp_level) 257 - { 258 - unsigned int resp; 259 - int ret; 260 - 261 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, 262 - 0, config_index, &resp); 263 - if (ret) 264 - return ret; 265 - 266 - ctdp_level->t_proc_hot = resp & GENMASK(7, 0); 267 - 268 - debug_printf( 269 - "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n", 270 - id->cpu, config_index, resp, ctdp_level->t_proc_hot); 271 - 272 - return 0; 135 + CHECK_CB(get_pwr_info); 136 + return isst_ops->get_pwr_info(id, config_index, ctdp_level); 273 137 } 274 138 275 139 int isst_get_coremask_info(struct isst_id *id, int config_index, 276 140 struct isst_pkg_ctdp_level_info *ctdp_level) 277 141 { 278 - unsigned int resp; 279 - int i, ret; 280 - 281 - ctdp_level->cpu_count = 0; 282 - for (i = 0; i < 2; ++i) { 283 - unsigned long long mask; 284 - int cpu_count = 0; 285 - 286 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 287 - CONFIG_TDP_GET_CORE_MASK, 0, 288 - (i << 8) | config_index, &resp); 289 - if (ret) 290 - return ret; 291 - 292 - debug_printf( 293 - "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n", 294 - id->cpu, config_index, i, resp); 295 - 296 - mask = (unsigned long long)resp << (32 * i); 297 - set_cpu_mask_from_punit_coremask(id, mask, 298 - ctdp_level->core_cpumask_size, 299 - ctdp_level->core_cpumask, 300 - &cpu_count); 301 - ctdp_level->cpu_count += cpu_count; 302 - debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", id->cpu, 303 - config_index, i, ctdp_level->cpu_count); 304 - } 305 - 306 - return 0; 142 + CHECK_CB(get_coremask_info); 143 + return isst_ops->get_coremask_info(id, config_index, ctdp_level); 307 144 } 308 145 309 146 int isst_get_get_trl_from_msr(struct isst_id *id, int *trl) ··· 166 329 167 330 int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) 168 331 { 169 - unsigned int req, resp; 170 - int ret; 171 - 172 - req = level | (avx_level << 16); 173 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 174 - CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, 175 - &resp); 176 - if (ret) 177 - return ret; 178 - 179 - debug_printf( 180 - "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n", 181 - id->cpu, req, resp); 182 - 183 - trl[0] = resp & GENMASK(7, 0); 184 - trl[1] = (resp & GENMASK(15, 8)) >> 8; 185 - trl[2] = (resp & GENMASK(23, 16)) >> 16; 186 - trl[3] = (resp & GENMASK(31, 24)) >> 24; 187 - 188 - req = level | BIT(8) | (avx_level << 16); 189 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 190 - CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, 191 - &resp); 192 - if (ret) 193 - return ret; 194 - 195 - debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", id->cpu, 196 - req, resp); 197 - 198 - trl[4] = resp & GENMASK(7, 0); 199 - trl[5] = (resp & GENMASK(15, 8)) >> 8; 200 - trl[6] = (resp & GENMASK(23, 16)) >> 16; 201 - trl[7] = (resp & GENMASK(31, 24)) >> 24; 202 - 203 - return 0; 332 + CHECK_CB(get_get_trl); 333 + return isst_ops->get_get_trl(id, level, avx_level, trl); 204 334 } 205 335 206 - int isst_get_trl_bucket_info(struct isst_id *id, unsigned long long *buckets_info) 336 + int isst_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level) 207 337 { 208 - int ret; 338 + CHECK_CB(get_get_trls); 339 + return isst_ops->get_get_trls(id, level, ctdp_level); 340 + } 209 341 210 - debug_printf("cpu:%d bucket info via MSR\n", id->cpu); 211 - 212 - *buckets_info = 0; 213 - 214 - ret = isst_send_msr_command(id->cpu, 0x1ae, 0, buckets_info); 215 - if (ret) 216 - return ret; 217 - 218 - debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", id->cpu, 219 - *buckets_info); 220 - 221 - return 0; 342 + int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) 343 + { 344 + CHECK_CB(get_trl_bucket_info); 345 + return isst_ops->get_trl_bucket_info(id, level, buckets_info); 222 346 } 223 347 224 348 int isst_set_tdp_level(struct isst_id *id, int tdp_level) 225 349 { 226 - unsigned int resp; 227 - int ret; 228 - 229 - 230 - if (isst_get_config_tdp_lock_status(id)) { 231 - isst_display_error_info_message(1, "TDP is locked", 0, 0); 232 - return -1; 233 - 234 - } 235 - 236 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, 237 - tdp_level, &resp); 238 - if (ret) { 239 - isst_display_error_info_message(1, "Set TDP level failed for level", 1, tdp_level); 240 - return ret; 241 - } 242 - 243 - return 0; 350 + CHECK_CB(set_tdp_level); 351 + return isst_ops->set_tdp_level(id, tdp_level); 244 352 } 245 353 246 354 int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info) 247 355 { 248 356 struct isst_pkg_ctdp_level_info ctdp_level; 249 357 struct isst_pkg_ctdp pkg_dev; 250 - int i, ret, max_punit_core, max_mask_index; 251 - unsigned int req, resp; 358 + int ret; 252 359 253 360 ret = isst_get_ctdp_levels(id, &pkg_dev); 254 361 if (ret) { ··· 216 435 217 436 pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask); 218 437 219 - max_punit_core = get_max_punit_core_id(id); 220 - max_mask_index = max_punit_core > 32 ? 2 : 1; 221 - 222 - for (i = 0; i < max_mask_index; ++i) { 223 - unsigned long long mask; 224 - int count; 225 - 226 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 227 - CONFIG_TDP_PBF_GET_CORE_MASK_INFO, 228 - 0, (i << 8) | level, &resp); 229 - if (ret) 230 - break; 231 - 232 - debug_printf( 233 - "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n", 234 - id->cpu, resp); 235 - 236 - mask = (unsigned long long)resp << (32 * i); 237 - set_cpu_mask_from_punit_coremask(id, mask, 238 - pbf_info->core_cpumask_size, 239 - pbf_info->core_cpumask, 240 - &count); 241 - } 242 - 243 - req = level; 244 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 245 - CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req, 246 - &resp); 247 - if (ret) 248 - return ret; 249 - 250 - debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", id->cpu, 251 - resp); 252 - 253 - pbf_info->p1_low = resp & 0xff; 254 - pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8; 255 - 256 - req = level; 257 - ret = isst_send_mbox_command( 258 - id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp); 259 - if (ret) 260 - return ret; 261 - 262 - debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", id->cpu, resp); 263 - 264 - pbf_info->tdp = resp & 0xffff; 265 - 266 - req = level; 267 - ret = isst_send_mbox_command( 268 - id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp); 269 - if (ret) 270 - return ret; 271 - 272 - debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", id->cpu, 273 - resp); 274 - pbf_info->t_control = (resp >> 8) & 0xff; 275 - pbf_info->t_prochot = resp & 0xff; 276 - 277 - return 0; 278 - } 279 - 280 - void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info) 281 - { 282 - free_cpu_set(pbf_info->core_cpumask); 438 + CHECK_CB(get_pbf_info); 439 + return isst_ops->get_pbf_info(id, level, pbf_info); 283 440 } 284 441 285 442 int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) 286 443 { 287 - struct isst_pkg_ctdp pkg_dev; 288 - struct isst_pkg_ctdp_level_info ctdp_level; 289 - int current_level; 290 - unsigned int req = 0, resp; 291 - int ret; 292 - 293 - ret = isst_get_ctdp_levels(id, &pkg_dev); 294 - if (ret) 295 - debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); 296 - 297 - current_level = pkg_dev.current_level; 298 - 299 - ret = isst_get_ctdp_control(id, current_level, &ctdp_level); 300 - if (ret) 301 - return ret; 302 - 303 - if (pbf) { 304 - if (ctdp_level.fact_enabled) 305 - req = BIT(16); 306 - 307 - if (enable) 308 - req |= BIT(17); 309 - else 310 - req &= ~BIT(17); 311 - } else { 312 - 313 - if (enable && !ctdp_level.sst_cp_enabled) 314 - isst_display_error_info_message(0, "Make sure to execute before: core-power enable", 0, 0); 315 - 316 - if (ctdp_level.pbf_enabled) 317 - req = BIT(17); 318 - 319 - if (enable) 320 - req |= BIT(16); 321 - else 322 - req &= ~BIT(16); 323 - } 324 - 325 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 326 - CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp); 327 - if (ret) 328 - return ret; 329 - 330 - debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n", 331 - id->cpu, pbf, req); 332 - 333 - return 0; 444 + CHECK_CB(set_pbf_fact_status); 445 + return isst_ops->set_pbf_fact_status(id, pbf, enable); 334 446 } 335 447 336 - int isst_get_fact_bucket_info(struct isst_id *id, int level, 337 - struct isst_fact_bucket_info *bucket_info) 338 - { 339 - unsigned int resp; 340 - int i, k, ret; 341 448 342 - for (i = 0; i < 2; ++i) { 343 - int j; 344 - 345 - ret = isst_send_mbox_command( 346 - id->cpu, CONFIG_TDP, 347 - CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0, 348 - (i << 8) | level, &resp); 349 - if (ret) 350 - return ret; 351 - 352 - debug_printf( 353 - "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n", 354 - id->cpu, i, level, resp); 355 - 356 - for (j = 0; j < 4; ++j) { 357 - bucket_info[j + (i * 4)].high_priority_cores_count = 358 - (resp >> (j * 8)) & 0xff; 359 - } 360 - } 361 - 362 - for (k = 0; k < 3; ++k) { 363 - for (i = 0; i < 2; ++i) { 364 - int j; 365 - 366 - ret = isst_send_mbox_command( 367 - id->cpu, CONFIG_TDP, 368 - CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0, 369 - (k << 16) | (i << 8) | level, &resp); 370 - if (ret) 371 - return ret; 372 - 373 - debug_printf( 374 - "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n", 375 - id->cpu, i, level, k, resp); 376 - 377 - for (j = 0; j < 4; ++j) { 378 - switch (k) { 379 - case 0: 380 - bucket_info[j + (i * 4)].sse_trl = 381 - (resp >> (j * 8)) & 0xff; 382 - break; 383 - case 1: 384 - bucket_info[j + (i * 4)].avx_trl = 385 - (resp >> (j * 8)) & 0xff; 386 - break; 387 - case 2: 388 - bucket_info[j + (i * 4)].avx512_trl = 389 - (resp >> (j * 8)) & 0xff; 390 - break; 391 - default: 392 - break; 393 - } 394 - } 395 - } 396 - } 397 - 398 - return 0; 399 - } 400 449 401 450 int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info) 402 451 { 403 452 struct isst_pkg_ctdp_level_info ctdp_level; 404 453 struct isst_pkg_ctdp pkg_dev; 405 - unsigned int resp; 406 - int j, ret, print; 454 + int ret; 407 455 408 456 ret = isst_get_ctdp_levels(id, &pkg_dev); 409 457 if (ret) { ··· 253 643 isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, level); 254 644 return -1; 255 645 } 256 - 257 - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, 258 - CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0, 259 - level, &resp); 260 - if (ret) 261 - return ret; 262 - 263 - debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n", 264 - id->cpu, resp); 265 - 266 - fact_info->lp_clipping_ratio_license_sse = resp & 0xff; 267 - fact_info->lp_clipping_ratio_license_avx2 = (resp >> 8) & 0xff; 268 - fact_info->lp_clipping_ratio_license_avx512 = (resp >> 16) & 0xff; 269 - 270 - ret = isst_get_fact_bucket_info(id, level, fact_info->bucket_info); 271 - if (ret) 272 - return ret; 273 - 274 - print = 0; 275 - for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { 276 - if (fact_bucket != 0xff && fact_bucket != j) 277 - continue; 278 - 279 - if (!fact_info->bucket_info[j].high_priority_cores_count) 280 - break; 281 - 282 - print = 1; 283 - } 284 - if (!print) { 285 - isst_display_error_info_message(1, "Invalid bucket", 0, 0); 286 - return -1; 287 - } 288 - 289 - return 0; 646 + CHECK_CB(get_fact_info); 647 + return isst_ops->get_fact_info(id, level, fact_bucket, fact_info); 290 648 } 291 649 292 650 int isst_get_trl(struct isst_id *id, unsigned long long *trl) ··· 286 708 { 287 709 unsigned long long msr_trl; 288 710 int ret; 711 + 712 + if (id->cpu < 0) 713 + return 0; 289 714 290 715 if (trl) { 291 716 msr_trl = trl; ··· 349 768 free_cpu_set(ctdp_level->pbf_info.core_cpumask); 350 769 free_cpu_set(ctdp_level->core_cpumask); 351 770 } 771 + } 772 + 773 + void isst_adjust_uncore_freq(struct isst_id *id, int config_index, 774 + struct isst_pkg_ctdp_level_info *ctdp_level) 775 + { 776 + CHECK_CB(adjust_uncore_freq); 777 + return isst_ops->adjust_uncore_freq(id, config_index, ctdp_level); 352 778 } 353 779 354 780 int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev) ··· 426 838 ctdp_level->tdp_ratio = ctdp_level->sse_p1; 427 839 } 428 840 429 - isst_get_get_trl_from_msr(id, ctdp_level->trl_sse_active_cores); 430 - isst_get_trl_bucket_info(id, &ctdp_level->buckets_info); 841 + isst_get_get_trl_from_msr(id, ctdp_level->trl_ratios[0]); 842 + isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores); 431 843 continue; 432 844 } 433 845 ··· 439 851 if (ret) 440 852 return ret; 441 853 442 - ret = isst_get_tjmax_info(id, i, ctdp_level); 443 - if (ret) 444 - return ret; 445 - 446 854 ctdp_level->core_cpumask_size = 447 855 alloc_cpu_set(&ctdp_level->core_cpumask); 448 856 ret = isst_get_coremask_info(id, i, ctdp_level); 449 857 if (ret) 450 858 return ret; 451 859 452 - ret = isst_get_trl_bucket_info(id, &ctdp_level->buckets_info); 860 + ret = isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores); 453 861 if (ret) 454 862 return ret; 455 863 456 - ret = isst_get_get_trl(id, i, 0, 457 - ctdp_level->trl_sse_active_cores); 864 + ret = isst_get_get_trls(id, i, ctdp_level); 458 865 if (ret) 459 866 return ret; 460 - 461 - ret = isst_get_get_trl(id, i, 1, 462 - ctdp_level->trl_avx_active_cores); 463 - if (ret) 464 - return ret; 465 - 466 - ret = isst_get_get_trl(id, i, 2, 467 - ctdp_level->trl_avx_512_active_cores); 468 - if (ret) 469 - return ret; 470 - 471 - isst_get_uncore_p0_p1_info(id, i, ctdp_level); 472 - isst_get_p1_info(id, i, ctdp_level); 473 - isst_get_uncore_mem_freq(id, i, ctdp_level); 474 867 } 475 868 476 869 if (!valid) ··· 462 893 463 894 int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type) 464 895 { 465 - unsigned int resp; 466 - int ret; 467 - 468 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, 469 - &resp); 470 - if (ret) 471 - return ret; 472 - 473 - debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); 474 - 475 - if (resp & BIT(1)) 476 - *enable = 1; 477 - else 478 - *enable = 0; 479 - 480 - if (resp & BIT(2)) 481 - *type = 1; 482 - else 483 - *type = 0; 484 - 485 - return 0; 896 + CHECK_CB(get_clos_information); 897 + return isst_ops->get_clos_information(id, enable, type); 486 898 } 487 899 488 900 int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) 489 901 { 490 - unsigned int req, resp; 491 - int ret; 492 - 493 - if (!enable_clos) { 494 - struct isst_pkg_ctdp pkg_dev; 495 - struct isst_pkg_ctdp_level_info ctdp_level; 496 - 497 - ret = isst_get_ctdp_levels(id, &pkg_dev); 498 - if (ret) { 499 - debug_printf("isst_get_ctdp_levels\n"); 500 - return ret; 501 - } 502 - 503 - ret = isst_get_ctdp_control(id, pkg_dev.current_level, 504 - &ctdp_level); 505 - if (ret) 506 - return ret; 507 - 508 - if (ctdp_level.fact_enabled) { 509 - isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0); 510 - return -EINVAL; 511 - } 512 - ret = isst_write_pm_config(id, 0); 513 - if (ret) 514 - isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); 515 - } else { 516 - ret = isst_write_pm_config(id, 1); 517 - if (ret) 518 - isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); 519 - } 520 - 521 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, 522 - &resp); 523 - if (ret) { 524 - isst_display_error_info_message(1, "CLOS_PM_QOS_CONFIG command failed", 0, 0); 525 - return ret; 526 - } 527 - 528 - debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); 529 - 530 - req = resp; 531 - 532 - if (enable_clos) 533 - req = req | BIT(1); 534 - else 535 - req = req & ~BIT(1); 536 - 537 - if (priority_type > 1) 538 - isst_display_error_info_message(1, "Invalid priority type: Changing type to ordered", 0, 0); 539 - 540 - if (priority_type) 541 - req = req | BIT(2); 542 - else 543 - req = req & ~BIT(2); 544 - 545 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 546 - BIT(MBOX_CMD_WRITE_BIT), req, &resp); 547 - if (ret) 548 - return ret; 549 - 550 - debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", id->cpu, 551 - priority_type, req); 552 - 553 - return 0; 902 + CHECK_CB(pm_qos_config); 903 + return isst_ops->pm_qos_config(id, enable_clos, priority_type); 554 904 } 555 905 556 906 int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) 557 907 { 558 - unsigned int resp; 559 - int ret; 560 - 561 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, 562 - &resp); 563 - if (ret) 564 - return ret; 565 - 566 - clos_config->epp = resp & 0x0f; 567 - clos_config->clos_prop_prio = (resp >> 4) & 0x0f; 568 - clos_config->clos_min = (resp >> 8) & 0xff; 569 - clos_config->clos_max = (resp >> 16) & 0xff; 570 - clos_config->clos_desired = (resp >> 24) & 0xff; 571 - 572 - return 0; 908 + CHECK_CB(pm_get_clos); 909 + return isst_ops->pm_get_clos(id, clos, clos_config); 573 910 } 574 911 575 912 int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) 576 913 { 577 - unsigned int req, resp; 578 - unsigned int param; 579 - int ret; 580 - 581 - req = clos_config->epp & 0x0f; 582 - req |= (clos_config->clos_prop_prio & 0x0f) << 4; 583 - req |= (clos_config->clos_min & 0xff) << 8; 584 - req |= (clos_config->clos_max & 0xff) << 16; 585 - req |= (clos_config->clos_desired & 0xff) << 24; 586 - 587 - param = BIT(MBOX_CMD_WRITE_BIT) | clos; 588 - 589 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, 590 - &resp); 591 - if (ret) 592 - return ret; 593 - 594 - debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", id->cpu, param, req); 595 - 596 - return 0; 914 + CHECK_CB(set_clos); 915 + return isst_ops->set_clos(id, clos, clos_config); 597 916 } 598 917 599 918 int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id) 600 919 { 601 - unsigned int resp; 602 - unsigned int param; 603 - int core_id, ret; 604 - 605 - core_id = find_phy_core_num(id->cpu); 606 - param = core_id; 607 - 608 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, 609 - &resp); 610 - if (ret) 611 - return ret; 612 - 613 - debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", id->cpu, param, 614 - resp); 615 - *clos_id = (resp >> 16) & 0x03; 616 - 617 - return 0; 920 + CHECK_CB(clos_get_assoc_status); 921 + return isst_ops->clos_get_assoc_status(id, clos_id); 618 922 } 619 923 620 924 int isst_clos_associate(struct isst_id *id, int clos_id) 621 925 { 622 - unsigned int req, resp; 623 - unsigned int param; 624 - int core_id, ret; 926 + CHECK_CB(clos_associate); 927 + return isst_ops->clos_associate(id, clos_id); 625 928 626 - req = (clos_id & 0x03) << 16; 627 - core_id = find_phy_core_num(id->cpu); 628 - param = BIT(MBOX_CMD_WRITE_BIT) | core_id; 629 - 630 - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 631 - req, &resp); 632 - if (ret) 633 - return ret; 634 - 635 - debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", id->cpu, param, 636 - req); 637 - 638 - return 0; 639 929 }
+26 -12
tools/power/x86/intel-speed-select/isst-daemon.c
··· 20 20 21 21 #include "isst.h" 22 22 23 - static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; 24 - static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; 23 + static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; 24 + static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; 25 25 26 26 static void init_levels(void) 27 27 { 28 - int i, j; 28 + int i, j, k; 29 29 30 30 for (i = 0; i < MAX_PACKAGE_COUNT; ++i) 31 31 for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j) 32 - per_package_levels_info[i][j] = -1; 32 + for (k = 0; k < MAX_PUNIT_PER_DIE; ++k) 33 + per_package_levels_info[i][j][k] = -1; 33 34 } 34 35 35 36 void process_level_change(struct isst_id *id) ··· 40 39 time_t tm; 41 40 int ret; 42 41 43 - if (id->pkg < 0 || id->die < 0) { 42 + if (id->pkg < 0 || id->die < 0 || id->punit < 0) { 44 43 debug_printf("Invalid package/die info for cpu:%d\n", id->cpu); 45 44 return; 46 45 } 47 46 48 47 tm = time(NULL); 49 - if (tm - per_package_levels_tm[id->pkg][id->die] < 2) 48 + if (tm - per_package_levels_tm[id->pkg][id->die][id->punit] < 2) 50 49 return; 51 50 52 - per_package_levels_tm[id->pkg][id->die] = tm; 51 + per_package_levels_tm[id->pkg][id->die][id->punit] = tm; 53 52 54 53 ret = isst_get_ctdp_levels(id, &pkg_dev); 55 54 if (ret) { ··· 65 64 return; 66 65 } 67 66 68 - if (per_package_levels_info[id->pkg][id->die] == pkg_dev.current_level) 67 + if (per_package_levels_info[id->pkg][id->die][id->punit] == pkg_dev.current_level) 69 68 return; 70 69 71 70 debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n", 72 - id->cpu, id->pkg, id->die, per_package_levels_info[id->pkg][id->die], 71 + id->cpu, id->pkg, id->die, per_package_levels_info[id->pkg][id->die][id->punit], 73 72 pkg_dev.current_level); 74 73 75 - per_package_levels_info[id->pkg][id->die] = pkg_dev.current_level; 74 + per_package_levels_info[id->pkg][id->die][id->punit] = pkg_dev.current_level; 76 75 77 76 ctdp_level.core_cpumask_size = 78 77 alloc_cpu_set(&ctdp_level.core_cpumask); ··· 83 82 return; 84 83 } 85 84 85 + if (use_cgroupv2()) { 86 + int ret; 87 + 88 + ret = enable_cpuset_controller(); 89 + if (ret) 90 + goto use_offline; 91 + 92 + isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, pkg_dev.current_level); 93 + 94 + goto free_mask; 95 + } 96 + 97 + use_offline: 86 98 if (ctdp_level.cpu_count) { 87 99 int i, max_cpus = get_topo_max_cpus(); 88 100 for (i = 0; i < max_cpus; ++i) { ··· 110 96 } 111 97 } 112 98 } 113 - 99 + free_mask: 114 100 free_cpu_set(ctdp_level.core_cpumask); 115 101 } 116 102 ··· 122 108 123 109 static void poll_for_config_change(void) 124 110 { 125 - for_each_online_package_in_set(_poll_for_config_change, NULL, NULL, 111 + for_each_online_power_domain_in_set(_poll_for_config_change, NULL, NULL, 126 112 NULL, NULL); 127 113 } 128 114
+110 -137
tools/power/x86/intel-speed-select/isst-display.c
··· 169 169 static int print_package_info(struct isst_id *id, FILE *outf) 170 170 { 171 171 char header[256]; 172 + int level = 1; 172 173 173 174 if (out_format_is_json()) { 174 - snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", 175 - id->pkg, id->die, id->cpu); 176 - format_and_print(outf, 1, header, NULL); 175 + if (api_version() > 1) 176 + snprintf(header, sizeof(header), "package-%d:die-%d:powerdomain-%d:cpu-%d", 177 + id->pkg, id->die, id->punit, id->cpu); 178 + else 179 + snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", 180 + id->pkg, id->die, id->cpu); 181 + format_and_print(outf, level, header, NULL); 177 182 return 1; 178 183 } 179 184 snprintf(header, sizeof(header), "package-%d", id->pkg); 180 - format_and_print(outf, 1, header, NULL); 185 + format_and_print(outf, level++, header, NULL); 181 186 snprintf(header, sizeof(header), "die-%d", id->die); 182 - format_and_print(outf, 2, header, NULL); 187 + format_and_print(outf, level++, header, NULL); 188 + if (api_version() > 1) { 189 + snprintf(header, sizeof(header), "powerdomain-%d", id->punit); 190 + format_and_print(outf, level++, header, NULL); 191 + } 183 192 snprintf(header, sizeof(header), "cpu-%d", id->cpu); 184 - format_and_print(outf, 3, header, NULL); 193 + format_and_print(outf, level, header, NULL); 185 194 186 - return 3; 195 + return level; 187 196 } 188 197 189 198 static void _isst_pbf_display_information(struct isst_id *id, FILE *outf, int level, ··· 207 198 208 199 snprintf(header, sizeof(header), "high-priority-base-frequency(MHz)"); 209 200 snprintf(value, sizeof(value), "%d", 210 - pbf_info->p1_high * DISP_FREQ_MULTIPLIER); 201 + pbf_info->p1_high * isst_get_disp_freq_multiplier()); 211 202 format_and_print(outf, disp_level + 1, header, value); 212 203 213 204 snprintf(header, sizeof(header), "high-priority-cpu-mask"); ··· 223 214 224 215 snprintf(header, sizeof(header), "low-priority-base-frequency(MHz)"); 225 216 snprintf(value, sizeof(value), "%d", 226 - pbf_info->p1_low * DISP_FREQ_MULTIPLIER); 217 + pbf_info->p1_low * isst_get_disp_freq_multiplier()); 227 218 format_and_print(outf, disp_level + 1, header, value); 228 219 229 220 if (is_clx_n_platform()) ··· 244 235 int base_level) 245 236 { 246 237 struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info; 238 + int trl_max_levels = isst_get_trl_max_levels(); 247 239 char header[256]; 248 240 char value[256]; 249 241 int print = 0, j; ··· 253 243 if (fact_bucket != 0xff && fact_bucket != j) 254 244 continue; 255 245 256 - if (!bucket_info[j].high_priority_cores_count) 246 + /* core count must be valid for CPU power domain */ 247 + if (!bucket_info[j].hp_cores && id->cpu >= 0) 257 248 break; 258 249 259 250 print = 1; ··· 267 256 snprintf(header, sizeof(header), "speed-select-turbo-freq-properties"); 268 257 format_and_print(outf, base_level, header, NULL); 269 258 for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { 259 + int i; 260 + 270 261 if (fact_bucket != 0xff && fact_bucket != j) 271 262 continue; 272 263 273 - if (!bucket_info[j].high_priority_cores_count) 264 + if (!bucket_info[j].hp_cores) 274 265 break; 275 266 276 267 snprintf(header, sizeof(header), "bucket-%d", j); ··· 280 267 281 268 snprintf(header, sizeof(header), "high-priority-cores-count"); 282 269 snprintf(value, sizeof(value), "%d", 283 - bucket_info[j].high_priority_cores_count); 270 + bucket_info[j].hp_cores); 284 271 format_and_print(outf, base_level + 2, header, value); 285 - 286 - if (fact_avx & 0x01) { 287 - snprintf(header, sizeof(header), 288 - "high-priority-max-frequency(MHz)"); 272 + for (i = 0; i < trl_max_levels; i++) { 273 + if (!bucket_info[j].hp_ratios[i] || (fact_avx != 0xFF && !(fact_avx & (1 << i)))) 274 + continue; 275 + if (i == 0 && api_version() == 1 && !is_emr_platform()) 276 + snprintf(header, sizeof(header), 277 + "high-priority-max-frequency(MHz)"); 278 + else 279 + snprintf(header, sizeof(header), 280 + "high-priority-max-%s-frequency(MHz)", isst_get_trl_level_name(i)); 289 281 snprintf(value, sizeof(value), "%d", 290 - bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER); 291 - format_and_print(outf, base_level + 2, header, value); 292 - } 293 - 294 - if (fact_avx & 0x02) { 295 - snprintf(header, sizeof(header), 296 - "high-priority-max-avx2-frequency(MHz)"); 297 - snprintf(value, sizeof(value), "%d", 298 - bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER); 299 - format_and_print(outf, base_level + 2, header, value); 300 - } 301 - 302 - if (fact_avx & 0x04) { 303 - snprintf(header, sizeof(header), 304 - "high-priority-max-avx512-frequency(MHz)"); 305 - snprintf(value, sizeof(value), "%d", 306 - bucket_info[j].avx512_trl * 307 - DISP_FREQ_MULTIPLIER); 282 + bucket_info[j].hp_ratios[i] * isst_get_disp_freq_multiplier()); 308 283 format_and_print(outf, base_level + 2, header, value); 309 284 } 310 285 } 311 286 snprintf(header, sizeof(header), 312 287 "speed-select-turbo-freq-clip-frequencies"); 313 288 format_and_print(outf, base_level + 1, header, NULL); 314 - snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); 315 - snprintf(value, sizeof(value), "%d", 316 - fact_info->lp_clipping_ratio_license_sse * 317 - DISP_FREQ_MULTIPLIER); 318 - format_and_print(outf, base_level + 2, header, value); 319 - snprintf(header, sizeof(header), 320 - "low-priority-max-avx2-frequency(MHz)"); 321 - snprintf(value, sizeof(value), "%d", 322 - fact_info->lp_clipping_ratio_license_avx2 * 323 - DISP_FREQ_MULTIPLIER); 324 - format_and_print(outf, base_level + 2, header, value); 325 - snprintf(header, sizeof(header), 326 - "low-priority-max-avx512-frequency(MHz)"); 327 - snprintf(value, sizeof(value), "%d", 328 - fact_info->lp_clipping_ratio_license_avx512 * 329 - DISP_FREQ_MULTIPLIER); 330 - format_and_print(outf, base_level + 2, header, value); 289 + 290 + for (j = 0; j < trl_max_levels; j++) { 291 + if (!fact_info->lp_ratios[j]) 292 + continue; 293 + 294 + /* No AVX level name for SSE to be consistent with previous formatting */ 295 + if (j == 0 && api_version() == 1 && !is_emr_platform()) 296 + snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); 297 + else 298 + snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)", 299 + isst_get_trl_level_name(j)); 300 + snprintf(value, sizeof(value), "%d", 301 + fact_info->lp_ratios[j] * isst_get_disp_freq_multiplier()); 302 + format_and_print(outf, base_level + 2, header, value); 303 + } 331 304 } 332 305 333 306 void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix, 334 307 unsigned int val, char *str0, char *str1) 335 308 { 336 - char header[256]; 337 309 char value[256]; 338 - int level = 1; 310 + int level = print_package_info(id, outf); 339 311 340 - if (out_format_is_json()) { 341 - snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", 342 - id->pkg, id->die, id->cpu); 343 - format_and_print(outf, level++, header, NULL); 344 - } else { 345 - snprintf(header, sizeof(header), "package-%d", id->pkg); 346 - format_and_print(outf, level++, header, NULL); 347 - snprintf(header, sizeof(header), "die-%d", id->die); 348 - format_and_print(outf, level++, header, NULL); 349 - snprintf(header, sizeof(header), "cpu-%d", id->cpu); 350 - format_and_print(outf, level++, header, NULL); 351 - } 312 + level++; 352 313 353 314 if (str0 && !val) 354 315 snprintf(value, sizeof(value), "%s", str0); ··· 341 354 char header[256]; 342 355 char value[512]; 343 356 static int level; 357 + int trl_max_levels = isst_get_trl_max_levels(); 344 358 int i; 345 359 346 360 if (pkg_dev->processed) ··· 349 361 350 362 for (i = 0; i <= pkg_dev->levels; ++i) { 351 363 struct isst_pkg_ctdp_level_info *ctdp_level; 352 - int j; 364 + int j, k; 353 365 354 366 ctdp_level = &pkg_dev->ctdp_level[i]; 355 367 if (!ctdp_level->processed) ··· 359 371 ctdp_level->level); 360 372 format_and_print(outf, level + 1, header, NULL); 361 373 362 - snprintf(header, sizeof(header), "cpu-count"); 363 - j = get_cpu_count(id); 364 - snprintf(value, sizeof(value), "%d", j); 365 - format_and_print(outf, level + 2, header, value); 366 - 367 - j = CPU_COUNT_S(ctdp_level->core_cpumask_size, 368 - ctdp_level->core_cpumask); 369 - if (j) { 370 - snprintf(header, sizeof(header), "enable-cpu-count"); 374 + if (id->cpu >= 0) { 375 + snprintf(header, sizeof(header), "cpu-count"); 376 + j = get_cpu_count(id); 371 377 snprintf(value, sizeof(value), "%d", j); 372 378 format_and_print(outf, level + 2, header, value); 373 - } 374 379 375 - if (ctdp_level->core_cpumask_size) { 376 - snprintf(header, sizeof(header), "enable-cpu-mask"); 377 - printcpumask(sizeof(value), value, 378 - ctdp_level->core_cpumask_size, 379 - ctdp_level->core_cpumask); 380 - format_and_print(outf, level + 2, header, value); 380 + j = CPU_COUNT_S(ctdp_level->core_cpumask_size, 381 + ctdp_level->core_cpumask); 382 + if (j) { 383 + snprintf(header, sizeof(header), "enable-cpu-count"); 384 + snprintf(value, sizeof(value), "%d", j); 385 + format_and_print(outf, level + 2, header, value); 386 + } 381 387 382 - snprintf(header, sizeof(header), "enable-cpu-list"); 383 - printcpulist(sizeof(value), value, 384 - ctdp_level->core_cpumask_size, 385 - ctdp_level->core_cpumask); 386 - format_and_print(outf, level + 2, header, value); 388 + if (ctdp_level->core_cpumask_size) { 389 + snprintf(header, sizeof(header), "enable-cpu-mask"); 390 + printcpumask(sizeof(value), value, 391 + ctdp_level->core_cpumask_size, 392 + ctdp_level->core_cpumask); 393 + format_and_print(outf, level + 2, header, value); 394 + 395 + snprintf(header, sizeof(header), "enable-cpu-list"); 396 + printcpulist(sizeof(value), value, 397 + ctdp_level->core_cpumask_size, 398 + ctdp_level->core_cpumask); 399 + format_and_print(outf, level + 2, header, value); 400 + } 387 401 } 388 402 389 403 snprintf(header, sizeof(header), "thermal-design-power-ratio"); ··· 396 406 if (!ctdp_level->sse_p1) 397 407 ctdp_level->sse_p1 = ctdp_level->tdp_ratio; 398 408 snprintf(value, sizeof(value), "%d", 399 - ctdp_level->sse_p1 * DISP_FREQ_MULTIPLIER); 409 + ctdp_level->sse_p1 * isst_get_disp_freq_multiplier()); 400 410 format_and_print(outf, level + 2, header, value); 401 411 402 412 if (ctdp_level->avx2_p1) { 403 413 snprintf(header, sizeof(header), "base-frequency-avx2(MHz)"); 404 414 snprintf(value, sizeof(value), "%d", 405 - ctdp_level->avx2_p1 * DISP_FREQ_MULTIPLIER); 415 + ctdp_level->avx2_p1 * isst_get_disp_freq_multiplier()); 406 416 format_and_print(outf, level + 2, header, value); 407 417 } 408 418 409 419 if (ctdp_level->avx512_p1) { 410 420 snprintf(header, sizeof(header), "base-frequency-avx512(MHz)"); 411 421 snprintf(value, sizeof(value), "%d", 412 - ctdp_level->avx512_p1 * DISP_FREQ_MULTIPLIER); 422 + ctdp_level->avx512_p1 * isst_get_disp_freq_multiplier()); 413 423 format_and_print(outf, level + 2, header, value); 414 424 } 415 425 416 426 if (ctdp_level->uncore_pm) { 417 427 snprintf(header, sizeof(header), "uncore-frequency-min(MHz)"); 418 428 snprintf(value, sizeof(value), "%d", 419 - ctdp_level->uncore_pm * DISP_FREQ_MULTIPLIER); 429 + ctdp_level->uncore_pm * isst_get_disp_freq_multiplier()); 420 430 format_and_print(outf, level + 2, header, value); 421 431 } 422 432 423 433 if (ctdp_level->uncore_p0) { 424 434 snprintf(header, sizeof(header), "uncore-frequency-max(MHz)"); 425 435 snprintf(value, sizeof(value), "%d", 426 - ctdp_level->uncore_p0 * DISP_FREQ_MULTIPLIER); 436 + ctdp_level->uncore_p0 * isst_get_disp_freq_multiplier()); 437 + format_and_print(outf, level + 2, header, value); 438 + } 439 + 440 + if (ctdp_level->amx_p1) { 441 + snprintf(header, sizeof(header), "base-frequency-amx(MHz)"); 442 + snprintf(value, sizeof(value), "%d", 443 + ctdp_level->amx_p1 * isst_get_disp_freq_multiplier()); 427 444 format_and_print(outf, level + 2, header, value); 428 445 } 429 446 430 447 if (ctdp_level->uncore_p1) { 431 448 snprintf(header, sizeof(header), "uncore-frequency-base(MHz)"); 432 449 snprintf(value, sizeof(value), "%d", 433 - ctdp_level->uncore_p1 * DISP_FREQ_MULTIPLIER); 450 + ctdp_level->uncore_p1 * isst_get_disp_freq_multiplier()); 434 451 format_and_print(outf, level + 2, header, value); 435 452 } 436 453 ··· 445 448 snprintf(header, sizeof(header), "mem-frequency(MHz)"); 446 449 snprintf(value, sizeof(value), "%d", 447 450 ctdp_level->mem_freq); 451 + format_and_print(outf, level + 2, header, value); 452 + } 453 + 454 + if (api_version() > 1) { 455 + snprintf(header, sizeof(header), "cooling_type"); 456 + snprintf(value, sizeof(value), "%d", 457 + ctdp_level->cooling_type); 448 458 format_and_print(outf, level + 2, header, value); 449 459 } 450 460 ··· 509 505 format_and_print(outf, level + 2, header, value); 510 506 } 511 507 512 - snprintf(header, sizeof(header), "turbo-ratio-limits-sse"); 513 - format_and_print(outf, level + 2, header, NULL); 514 - for (j = 0; j < 8; ++j) { 515 - snprintf(header, sizeof(header), "bucket-%d", j); 516 - format_and_print(outf, level + 3, header, NULL); 508 + for (k = 0; k < trl_max_levels; k++) { 509 + if (!ctdp_level->trl_ratios[k][0]) 510 + continue; 517 511 518 - snprintf(header, sizeof(header), "core-count"); 519 - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); 520 - format_and_print(outf, level + 4, header, value); 521 - 522 - snprintf(header, sizeof(header), 523 - "max-turbo-frequency(MHz)"); 524 - snprintf(value, sizeof(value), "%d", 525 - ctdp_level->trl_sse_active_cores[j] * 526 - DISP_FREQ_MULTIPLIER); 527 - format_and_print(outf, level + 4, header, value); 528 - } 529 - 530 - if (ctdp_level->trl_avx_active_cores[0]) { 531 - snprintf(header, sizeof(header), "turbo-ratio-limits-avx2"); 512 + snprintf(header, sizeof(header), "turbo-ratio-limits-%s", isst_get_trl_level_name(k)); 532 513 format_and_print(outf, level + 2, header, NULL); 514 + 533 515 for (j = 0; j < 8; ++j) { 534 516 snprintf(header, sizeof(header), "bucket-%d", j); 535 517 format_and_print(outf, level + 3, header, NULL); 536 518 537 519 snprintf(header, sizeof(header), "core-count"); 538 - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); 520 + 521 + snprintf(value, sizeof(value), "%llu", (ctdp_level->trl_cores >> (j * 8)) & 0xff); 539 522 format_and_print(outf, level + 4, header, value); 540 523 541 524 snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); 542 - snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_active_cores[j] * DISP_FREQ_MULTIPLIER); 543 - format_and_print(outf, level + 4, header, value); 544 - } 545 - } 546 - 547 - if (ctdp_level->trl_avx_512_active_cores[0]) { 548 - snprintf(header, sizeof(header), "turbo-ratio-limits-avx512"); 549 - format_and_print(outf, level + 2, header, NULL); 550 - for (j = 0; j < 8; ++j) { 551 - snprintf(header, sizeof(header), "bucket-%d", j); 552 - format_and_print(outf, level + 3, header, NULL); 553 - 554 - snprintf(header, sizeof(header), "core-count"); 555 - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); 556 - format_and_print(outf, level + 4, header, value); 557 - 558 - snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); 559 - snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_512_active_cores[j] * DISP_FREQ_MULTIPLIER); 525 + snprintf(value, sizeof(value), "%d", ctdp_level->trl_ratios[k][j] * isst_get_disp_freq_multiplier()); 560 526 format_and_print(outf, level + 4, header, value); 561 527 } 562 528 } ··· 605 631 format_and_print(outf, level + 2, header, value); 606 632 607 633 snprintf(header, sizeof(header), "clos-min"); 608 - snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * DISP_FREQ_MULTIPLIER); 634 + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * isst_get_disp_freq_multiplier()); 609 635 format_and_print(outf, level + 2, header, value); 610 636 611 637 snprintf(header, sizeof(header), "clos-max"); 612 - if (clos_config->clos_max == 0xff) 638 + if ((clos_config->clos_max * isst_get_disp_freq_multiplier()) == 25500) 613 639 snprintf(value, sizeof(value), "Max Turbo frequency"); 614 640 else 615 - snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * DISP_FREQ_MULTIPLIER); 641 + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * isst_get_disp_freq_multiplier()); 616 642 format_and_print(outf, level + 2, header, value); 617 643 618 644 snprintf(header, sizeof(header), "clos-desired"); 619 - snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * DISP_FREQ_MULTIPLIER); 645 + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * isst_get_disp_freq_multiplier()); 620 646 format_and_print(outf, level + 2, header, value); 621 647 622 648 format_and_print(outf, level, NULL, NULL); ··· 691 717 char value[256]; 692 718 int level = 3; 693 719 694 - if (id->cpu >= 0) 695 - level = print_package_info(id, outf); 720 + level = print_package_info(id, outf); 696 721 697 722 snprintf(header, sizeof(header), "%s", feature); 698 723 format_and_print(outf, level + 1, header, NULL);
+70 -26
tools/power/x86/intel-speed-select/isst.h
··· 28 28 #include <stdarg.h> 29 29 #include <sys/ioctl.h> 30 30 31 + #include <linux/isst_if.h> 32 + 31 33 #define BIT(x) (1 << (x)) 32 34 #define BIT_ULL(nr) (1ULL << (nr)) 33 35 #define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h)))) ··· 79 77 80 78 #define DISP_FREQ_MULTIPLIER 100 81 79 82 - #define MAX_PACKAGE_COUNT 8 83 - #define MAX_DIE_PER_PACKAGE 2 80 + #define MAX_PACKAGE_COUNT 8 81 + #define MAX_DIE_PER_PACKAGE 2 82 + #define MAX_PUNIT_PER_DIE 8 84 83 85 84 /* Unified structure to specific a CPU or a Power Domain */ 86 85 struct isst_id { 87 86 int cpu; 88 87 int pkg; 89 88 int die; 89 + int punit; 90 90 }; 91 91 92 92 struct isst_clos_config { 93 + unsigned int clos_min; 94 + unsigned int clos_max; 93 95 unsigned char epp; 94 96 unsigned char clos_prop_prio; 95 - unsigned char clos_min; 96 - unsigned char clos_max; 97 97 unsigned char clos_desired; 98 98 }; 99 99 100 100 struct isst_fact_bucket_info { 101 - int high_priority_cores_count; 102 - int sse_trl; 103 - int avx_trl; 104 - int avx512_trl; 101 + int hp_cores; 102 + int hp_ratios[TRL_MAX_LEVELS]; 105 103 }; 106 104 107 105 struct isst_pbf_info { ··· 119 117 #define ISST_TRL_MAX_ACTIVE_CORES 8 120 118 #define ISST_FACT_MAX_BUCKETS 8 121 119 struct isst_fact_info { 122 - int lp_clipping_ratio_license_sse; 123 - int lp_clipping_ratio_license_avx2; 124 - int lp_clipping_ratio_license_avx512; 120 + int lp_ratios[TRL_MAX_LEVELS]; 125 121 struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS]; 126 122 }; 127 123 ··· 143 143 int pkg_max_power; 144 144 int fact; 145 145 int t_proc_hot; 146 + int cooling_type; 146 147 int uncore_p0; 147 148 int uncore_p1; 148 149 int uncore_pm; 149 150 int sse_p1; 150 151 int avx2_p1; 151 152 int avx512_p1; 153 + int amx_p1; 152 154 int mem_freq; 153 155 size_t core_cpumask_size; 154 156 cpu_set_t *core_cpumask; 155 157 int cpu_count; 156 - unsigned long long buckets_info; 157 - int trl_sse_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; 158 - int trl_avx_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; 159 - int trl_avx_512_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; 158 + unsigned long long trl_cores; /* Buckets info */ 159 + int trl_ratios[TRL_MAX_LEVELS][ISST_TRL_MAX_ACTIVE_CORES]; 160 160 int kobj_bucket_index; 161 161 int active_bucket; 162 162 int fact_max_index; ··· 178 178 struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS]; 179 179 }; 180 180 181 + enum isst_platform_param { 182 + ISST_PARAM_MBOX_DELAY, 183 + ISST_PARAM_MBOX_RETRIES, 184 + }; 185 + 186 + struct isst_platform_ops { 187 + int (*get_disp_freq_multiplier)(void); 188 + int (*get_trl_max_levels)(void); 189 + char *(*get_trl_level_name)(int level); 190 + void (*update_platform_param)(enum isst_platform_param param, int value); 191 + int (*is_punit_valid)(struct isst_id *id); 192 + int (*read_pm_config)(struct isst_id *id, int *cp_state, int *cp_cap); 193 + int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); 194 + int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); 195 + int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); 196 + int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); 197 + int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); 198 + int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); 199 + int (*get_get_trls)(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level); 200 + int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); 201 + int (*set_tdp_level)(struct isst_id *id, int tdp_level); 202 + int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); 203 + int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); 204 + int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); 205 + void (*adjust_uncore_freq)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); 206 + int (*get_clos_information)(struct isst_id *id, int *enable, int *type); 207 + int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); 208 + int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); 209 + int (*set_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); 210 + int (*clos_get_assoc_status)(struct isst_id *id, int *clos_id); 211 + int (*clos_associate)(struct isst_id *id, int clos_id); 212 + }; 213 + 181 214 extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); 182 215 extern int get_topo_max_cpus(void); 183 216 extern int get_cpu_count(struct isst_id *id); 184 217 extern int get_max_punit_core_id(struct isst_id *id); 218 + extern int api_version(void); 185 219 186 220 /* Common interfaces */ 187 221 FILE *get_output_file(void); 222 + extern int is_debug_enabled(void); 188 223 extern void debug_printf(const char *format, ...); 189 224 extern int out_format_is_json(void); 190 225 extern void set_isst_id(struct isst_id *id, int cpu); ··· 231 196 size_t core_cpumask_size, 232 197 cpu_set_t *core_cpumask, 233 198 int *cpu_cnt); 234 - 235 - extern int isst_send_mbox_command(unsigned int cpu, unsigned char command, 236 - unsigned char sub_command, 237 - unsigned int write, 238 - unsigned int req_data, unsigned int *resp); 239 - 240 199 extern int isst_send_msr_command(unsigned int cpu, unsigned int command, 241 200 int write, unsigned long long *req_resp); 201 + 202 + extern int isst_set_platform_ops(int api_version); 203 + extern void isst_update_platform_param(enum isst_platform_param, int vale); 204 + extern int isst_get_disp_freq_multiplier(void); 205 + extern int isst_get_trl_max_levels(void); 206 + extern char *isst_get_trl_level_name(int level); 207 + extern int isst_is_punit_valid(struct isst_id *id); 242 208 243 209 extern int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev); 244 210 extern int isst_get_ctdp_control(struct isst_id *id, int config_index, 245 211 struct isst_pkg_ctdp_level_info *ctdp_level); 246 212 extern int isst_get_coremask_info(struct isst_id *id, int config_index, 247 213 struct isst_pkg_ctdp_level_info *ctdp_level); 248 - extern void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, 214 + extern void isst_adjust_uncore_freq(struct isst_id *id, int config_index, 249 215 struct isst_pkg_ctdp_level_info *ctdp_level); 250 216 extern int isst_get_process_ctdp(struct isst_id *id, int tdp_level, 251 217 struct isst_pkg_ctdp *pkg_dev); ··· 264 228 extern int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable); 265 229 extern int isst_get_pbf_info(struct isst_id *id, int level, 266 230 struct isst_pbf_info *pbf_info); 267 - extern void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info); 268 231 extern int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, 269 232 struct isst_fact_info *fact_info); 270 - extern int isst_get_fact_bucket_info(struct isst_id *id, int level, 271 - struct isst_fact_bucket_info *bucket_info); 272 233 extern void isst_fact_display_information(struct isst_id *id, FILE *outf, int level, 273 234 int fact_bucket, int fact_avx, 274 235 struct isst_fact_info *fact_info); ··· 298 265 extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg); 299 266 extern int is_skx_based_platform(void); 300 267 extern int is_spr_platform(void); 268 + extern int is_emr_platform(void); 301 269 extern int is_icx_platform(void); 302 270 extern void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl); 303 271 304 272 extern void set_cpu_online_offline(int cpu, int state); 305 - extern void for_each_online_package_in_set(void (*callback)(struct isst_id *, void *, void *, 273 + extern void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *, 306 274 void *, void *), 307 275 void *arg1, void *arg2, void *arg3, 308 276 void *arg4); ··· 311 277 extern void process_level_change(struct isst_id *id); 312 278 extern int hfi_main(void); 313 279 extern void hfi_exit(void); 280 + 281 + /* Interface specific callbacks */ 282 + extern struct isst_platform_ops *mbox_get_platform_ops(void); 283 + extern struct isst_platform_ops *tpmi_get_platform_ops(void); 284 + 285 + /* Cgroup related interface */ 286 + extern int enable_cpuset_controller(void); 287 + extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level); 288 + extern int use_cgroupv2(void); 289 + 314 290 #endif