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

platform/x86: thinkpad_acpi: sysfs interface to get wwan antenna type

On some newer Thinkpads we need to set SAR value based on antenna type.
This patch provides a sysfs interface that userspace can use to get
antenna type and set corresponding SAR value, as is required for FCC
certification.

Reviewed-by: Mark Pearson <markpearson@lenovo.com>
Signed-off-by: Nitin Joshi <njoshi1@lenovo.com>
Link: https://lore.kernel.org/r/20210317024636.356175-1-njoshi1@lenovo.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Nitin Joshi and committed by
Hans de Goede
3feb52a2 2728f39d

+129
+20
Documentation/admin-guide/laptops/thinkpad-acpi.rst
··· 52 52 - LCD Shadow (PrivacyGuard) enable and disable 53 53 - Lap mode sensor 54 54 - Setting keyboard language 55 + - WWAN Antenna type 55 56 56 57 A compatibility table by model and feature is maintained on the web 57 58 site, http://ibm-acpi.sf.net/. I appreciate any success or failure ··· 1491 1490 nl(Dutch), nn(Norway), pl(Polish), pt(portugese), sl(Slovenian), sv(Sweden), 1492 1491 tr(Turkey) 1493 1492 1493 + WWAN Antenna type 1494 + ----------------- 1495 + 1496 + sysfs: wwan_antenna_type 1497 + 1498 + On some newer Thinkpads we need to set SAR value based on the antenna 1499 + type. This interface will be used by userspace to get the antenna type 1500 + and set the corresponding SAR value, as is required for FCC certification. 1501 + 1502 + The available commands are:: 1503 + 1504 + cat /sys/devices/platform/thinkpad_acpi/wwan_antenna_type 1505 + 1506 + Currently 2 antenna types are supported as mentioned below: 1507 + - type a 1508 + - type b 1509 + 1510 + The property is read-only. If the platform doesn't have support the sysfs 1511 + class is not created. 1494 1512 1495 1513 Adaptive keyboard 1496 1514 -----------------
+109
drivers/platform/x86/thinkpad_acpi.c
··· 10496 10496 .exit = kbdlang_exit, 10497 10497 }; 10498 10498 10499 + /************************************************************************* 10500 + * DPRC(Dynamic Power Reduction Control) subdriver, for the Lenovo WWAN 10501 + * and WLAN feature. 10502 + */ 10503 + #define DPRC_GET_WWAN_ANTENNA_TYPE 0x40000 10504 + #define DPRC_WWAN_ANTENNA_TYPE_A_BIT BIT(4) 10505 + #define DPRC_WWAN_ANTENNA_TYPE_B_BIT BIT(8) 10506 + static bool has_antennatype; 10507 + static int wwan_antennatype; 10508 + 10509 + static int dprc_command(int command, int *output) 10510 + { 10511 + acpi_handle dprc_handle; 10512 + 10513 + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DPRC", &dprc_handle))) { 10514 + /* Platform doesn't support DPRC */ 10515 + return -ENODEV; 10516 + } 10517 + 10518 + if (!acpi_evalf(dprc_handle, output, NULL, "dd", command)) 10519 + return -EIO; 10520 + 10521 + /* 10522 + * METHOD_ERR gets returned on devices where few commands are not supported 10523 + * for example command to get WWAN Antenna type command is not supported on 10524 + * some devices. 10525 + */ 10526 + if (*output & METHOD_ERR) 10527 + return -ENODEV; 10528 + 10529 + return 0; 10530 + } 10531 + 10532 + static int get_wwan_antenna(int *wwan_antennatype) 10533 + { 10534 + int output, err; 10535 + 10536 + /* Get current Antenna type */ 10537 + err = dprc_command(DPRC_GET_WWAN_ANTENNA_TYPE, &output); 10538 + if (err) 10539 + return err; 10540 + 10541 + if (output & DPRC_WWAN_ANTENNA_TYPE_A_BIT) 10542 + *wwan_antennatype = 1; 10543 + else if (output & DPRC_WWAN_ANTENNA_TYPE_B_BIT) 10544 + *wwan_antennatype = 2; 10545 + else 10546 + return -ENODEV; 10547 + 10548 + return 0; 10549 + } 10550 + 10551 + /* sysfs wwan antenna type entry */ 10552 + static ssize_t wwan_antenna_type_show(struct device *dev, 10553 + struct device_attribute *attr, 10554 + char *buf) 10555 + { 10556 + switch (wwan_antennatype) { 10557 + case 1: 10558 + return sysfs_emit(buf, "type a\n"); 10559 + case 2: 10560 + return sysfs_emit(buf, "type b\n"); 10561 + default: 10562 + return -ENODATA; 10563 + } 10564 + } 10565 + static DEVICE_ATTR_RO(wwan_antenna_type); 10566 + 10567 + static int tpacpi_dprc_init(struct ibm_init_struct *iibm) 10568 + { 10569 + int wwanantenna_err, err; 10570 + 10571 + wwanantenna_err = get_wwan_antenna(&wwan_antennatype); 10572 + /* 10573 + * If support isn't available (ENODEV) then quit, but don't 10574 + * return an error. 10575 + */ 10576 + if (wwanantenna_err == -ENODEV) 10577 + return 0; 10578 + 10579 + /* if there was an error return it */ 10580 + if (wwanantenna_err && (wwanantenna_err != -ENODEV)) 10581 + return wwanantenna_err; 10582 + else if (!wwanantenna_err) 10583 + has_antennatype = true; 10584 + 10585 + if (has_antennatype) { 10586 + err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr); 10587 + if (err) 10588 + return err; 10589 + } 10590 + return 0; 10591 + } 10592 + 10593 + static void dprc_exit(void) 10594 + { 10595 + if (has_antennatype) 10596 + sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr); 10597 + } 10598 + 10599 + static struct ibm_struct dprc_driver_data = { 10600 + .name = "dprc", 10601 + .exit = dprc_exit, 10602 + }; 10603 + 10499 10604 /**************************************************************************** 10500 10605 **************************************************************************** 10501 10606 * ··· 11104 10999 { 11105 11000 .init = tpacpi_kbdlang_init, 11106 11001 .data = &kbdlang_driver_data, 11002 + }, 11003 + { 11004 + .init = tpacpi_dprc_init, 11005 + .data = &dprc_driver_data, 11107 11006 }, 11108 11007 }; 11109 11008