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>
This commit is contained in:
Nitin Joshi 2021-03-17 11:46:36 +09:00 committed by Hans de Goede
parent 2728f39dfc
commit 3feb52a2b8
2 changed files with 129 additions and 0 deletions

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

View 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)