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:
committed by
Hans de Goede
parent
2728f39dfc
commit
3feb52a2b8
@@ -52,6 +52,7 @@ detailed description):
|
|||||||
- LCD Shadow (PrivacyGuard) enable and disable
|
- LCD Shadow (PrivacyGuard) enable and disable
|
||||||
- Lap mode sensor
|
- Lap mode sensor
|
||||||
- Setting keyboard language
|
- Setting keyboard language
|
||||||
|
- WWAN Antenna type
|
||||||
|
|
||||||
A compatibility table by model and feature is maintained on the web
|
A compatibility table by model and feature is maintained on the web
|
||||||
site, http://ibm-acpi.sf.net/. I appreciate any success or failure
|
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),
|
nl(Dutch), nn(Norway), pl(Polish), pt(portugese), sl(Slovenian), sv(Sweden),
|
||||||
tr(Turkey)
|
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
|
Adaptive keyboard
|
||||||
-----------------
|
-----------------
|
||||||
|
|||||||
@@ -10496,6 +10496,111 @@ static struct ibm_struct kbdlang_driver_data = {
|
|||||||
.exit = kbdlang_exit,
|
.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,
|
.init = tpacpi_kbdlang_init,
|
||||||
.data = &kbdlang_driver_data,
|
.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)
|
static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
|
||||||
|
|||||||
Reference in New Issue
Block a user