OSDN Git Service

platform/x86: thinkpad_acpi: sysfs interface to get wwan antenna type
authorNitin Joshi <nitjoshi@gmail.com>
Wed, 17 Mar 2021 02:46:36 +0000 (11:46 +0900)
committerHans de Goede <hdegoede@redhat.com>
Sun, 21 Mar 2021 17:04:19 +0000 (18:04 +0100)
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>
Documentation/admin-guide/laptops/thinkpad-acpi.rst
drivers/platform/x86/thinkpad_acpi.c

index 91fd684..6721a80 100644 (file)
@@ -52,6 +52,7 @@ detailed description):
        - LCD Shadow (PrivacyGuard) enable and disable
        - Lap mode sensor
        - Setting keyboard language
+       - WWAN Antenna type
 
 A compatibility table by model and feature is maintained on the web
 site, http://ibm-acpi.sf.net/. I appreciate any success or failure
@@ -1490,6 +1491,25 @@ fr(French), fr-ch(French(Switzerland)), hu(Hungarian), it(Italy), jp (Japan),
 nl(Dutch), nn(Norway), pl(Polish), pt(portugese), sl(Slovenian), sv(Sweden),
 tr(Turkey)
 
+WWAN Antenna type
+-----------------
+
+sysfs: wwan_antenna_type
+
+On some newer Thinkpads we need to set SAR value based on the antenna
+type. This interface will be used by userspace to get the antenna type
+and set the corresponding SAR value, as is required for FCC certification.
+
+The available commands are::
+
+        cat /sys/devices/platform/thinkpad_acpi/wwan_antenna_type
+
+Currently 2 antenna types are supported as mentioned below:
+- type a
+- type b
+
+The property is read-only. If the platform doesn't have support the sysfs
+class is not created.
 
 Adaptive keyboard
 -----------------
index 67b4898..1b403c1 100644 (file)
@@ -10496,6 +10496,111 @@ static struct ibm_struct kbdlang_driver_data = {
        .exit = kbdlang_exit,
 };
 
+/*************************************************************************
+ * DPRC(Dynamic Power Reduction Control) subdriver, for the Lenovo WWAN
+ * and WLAN feature.
+ */
+#define DPRC_GET_WWAN_ANTENNA_TYPE      0x40000
+#define DPRC_WWAN_ANTENNA_TYPE_A_BIT    BIT(4)
+#define DPRC_WWAN_ANTENNA_TYPE_B_BIT    BIT(8)
+static bool has_antennatype;
+static int wwan_antennatype;
+
+static int dprc_command(int command, int *output)
+{
+       acpi_handle dprc_handle;
+
+       if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DPRC", &dprc_handle))) {
+               /* Platform doesn't support DPRC */
+               return -ENODEV;
+       }
+
+       if (!acpi_evalf(dprc_handle, output, NULL, "dd", command))
+               return -EIO;
+
+       /*
+        * METHOD_ERR gets returned on devices where few commands are not supported
+        * for example command to get WWAN Antenna type command is not supported on
+        * some devices.
+        */
+       if (*output & METHOD_ERR)
+               return -ENODEV;
+
+       return 0;
+}
+
+static int get_wwan_antenna(int *wwan_antennatype)
+{
+       int output, err;
+
+       /* Get current Antenna type */
+       err = dprc_command(DPRC_GET_WWAN_ANTENNA_TYPE, &output);
+       if (err)
+               return err;
+
+       if (output & DPRC_WWAN_ANTENNA_TYPE_A_BIT)
+               *wwan_antennatype = 1;
+       else if (output & DPRC_WWAN_ANTENNA_TYPE_B_BIT)
+               *wwan_antennatype = 2;
+       else
+               return -ENODEV;
+
+       return 0;
+}
+
+/* sysfs wwan antenna type entry */
+static ssize_t wwan_antenna_type_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       switch (wwan_antennatype) {
+       case 1:
+               return sysfs_emit(buf, "type a\n");
+       case 2:
+               return sysfs_emit(buf, "type b\n");
+       default:
+               return -ENODATA;
+       }
+}
+static DEVICE_ATTR_RO(wwan_antenna_type);
+
+static int tpacpi_dprc_init(struct ibm_init_struct *iibm)
+{
+       int wwanantenna_err, err;
+
+       wwanantenna_err = get_wwan_antenna(&wwan_antennatype);
+       /*
+        * If support isn't available (ENODEV) then quit, but don't
+        * return an error.
+        */
+       if (wwanantenna_err == -ENODEV)
+               return 0;
+
+       /* if there was an error return it */
+       if (wwanantenna_err && (wwanantenna_err != -ENODEV))
+               return wwanantenna_err;
+       else if (!wwanantenna_err)
+               has_antennatype = true;
+
+       if (has_antennatype) {
+               err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static void dprc_exit(void)
+{
+       if (has_antennatype)
+               sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr);
+}
+
+static struct ibm_struct dprc_driver_data = {
+       .name = "dprc",
+       .exit = dprc_exit,
+};
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -11000,6 +11105,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
                .init = tpacpi_kbdlang_init,
                .data = &kbdlang_driver_data,
        },
+       {
+               .init = tpacpi_dprc_init,
+               .data = &dprc_driver_data,
+       },
 };
 
 static int __init set_ibm_param(const char *val, const struct kernel_param *kp)