Merge tag 'platform-drivers-x86-v5.8-1' of git://git.infradead.org/linux-platform-drivers-x86

Pull x86 platform driver updates from Andy Shevchenko:

 - Add a support of the media keys on the ASUS laptop UX325JA/UX425JA

 - ASUS WMI driver can now handle 2-in-1 models T100TA, T100CHI, T100HA,
   T200TA

 - Big refactoring of Intel SCU driver with Elkhart Lake support has
   been added

 - Slim Bootloarder firmware update signaling WMI driver has been added

 - Thinkpad ACPI driver can handle dual fan configuration on new P and X
   models

 - Touchscreen DMI driver has been extended to support
    - MP-man MPWIN895CL tablet
    - ONDA V891 v5 tablet
    - techBite Arc 11.6
    - Trekstor Twin 10.1
    - Trekstor Yourbook C11B
    - Vinga J116

 - Virtual Button driver got a few fixes to detect mode of 2-in-1 tablet
   models

 - Intel Speed Select tools update

 - Plenty of small cleanups here and there

* tag 'platform-drivers-x86-v5.8-1' of git://git.infradead.org/linux-platform-drivers-x86: (89 commits)
  platform/x86: dcdbas: Check SMBIOS for protected buffer address
  platform/x86: asus_wmi: Reserve more space for struct bias_args
  platform/x86: intel-vbtn: Only blacklist SW_TABLET_MODE on the 9 / "Laptop" chasis-type
  platform/x86: intel-hid: Add a quirk to support HP Spectre X2 (2015)
  platform/x86: touchscreen_dmi: Update Trekstor Twin 10.1 entry
  platform/x86: touchscreen_dmi: Add info for the Trekstor Yourbook C11B
  platform/x86: hp-wmi: Introduce HPWMI_POWER_FW_OR_HW as convenient shortcut
  platform/x86: hp-wmi: Convert simple_strtoul() to kstrtou32()
  platform/x86: hp-wmi: Refactor postcode_store() to follow standard patterns
  platform/x86: acerhdf: replace space by * in modalias
  platform/x86: ISST: Increase timeout
  tools/power/x86/intel-speed-select: Fix invalid core mask
  tools/power/x86/intel-speed-select: Increase CPU count
  tools/power/x86/intel-speed-select: Fix json perf-profile output output
  platform/x86: dell-wmi: Ignore keyboard attached / detached events
  platform/x86: dell-laptop: don't register micmute LED if there is no token
  platform/x86: thinkpad_acpi: Replace custom approach by kstrtoint()
  platform/x86: thinkpad_acpi: Use strndup_user() in dispatch_proc_write()
  platform/x86: thinkpad_acpi: Replace next_cmd(&buf) with strsep(&buf, ",")
  platform/x86: intel-vbtn: Detect switch position before registering the input-device
  ...
This commit is contained in:
Linus Torvalds
2020-06-02 12:56:58 -07:00
melakukan a5a82e0a59
63 mengubah file dengan 2331 tambahan dan 1708 penghapusan

Melihat File

@@ -78,6 +78,16 @@ config HUAWEI_WMI
To compile this driver as a module, choose M here: the module
will be called huawei-wmi.
config INTEL_WMI_SBL_FW_UPDATE
tristate "Intel WMI Slim Bootloader firmware update signaling driver"
depends on ACPI_WMI
help
Say Y here if you want to be able to use the WMI interface to signal
Slim Bootloader to trigger update on next reboot.
To compile this driver as a module, choose M here: the module will
be called intel-wmi-sbl-fw-update.
config INTEL_WMI_THUNDERBOLT
tristate "Intel WMI thunderbolt force power driver"
depends on ACPI_WMI
@@ -1269,7 +1279,8 @@ config INTEL_UNCORE_FREQ_CONTROL
config INTEL_BXTWC_PMIC_TMU
tristate "Intel BXT Whiskey Cove TMU Driver"
depends on REGMAP
depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC
depends on MFD_INTEL_PMC_BXT
depends on INTEL_SOC_PMIC_BXTWC
---help---
Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
This driver enables the alarm wakeup functionality in the TMU unit
@@ -1295,7 +1306,7 @@ config INTEL_MFLD_THERMAL
config INTEL_MID_POWER_BUTTON
tristate "power button driver for Intel MID platforms"
depends on INTEL_SCU_IPC && INPUT
depends on INTEL_SCU && INPUT
help
This driver handles the power button on the Intel MID platforms.
@@ -1327,14 +1338,6 @@ config INTEL_PMC_CORE
- LTR Ignore
- MPHY/PLL gating status (Sunrisepoint PCH only)
config INTEL_PMC_IPC
tristate "Intel PMC IPC Driver"
depends on ACPI && PCI
---help---
This driver provides support for PMC control on some Intel platforms.
The PMC is an ARC processor which defines IPC commands for communication
with other entities in the CPU.
config INTEL_PUNIT_IPC
tristate "Intel P-Unit IPC Driver"
---help---
@@ -1342,17 +1345,39 @@ config INTEL_PUNIT_IPC
which is used to bridge the communications between kernel and P-Unit.
config INTEL_SCU_IPC
bool "Intel SCU IPC Support"
depends on X86_INTEL_MID
default y
---help---
IPC is used to bridge the communications between kernel and SCU on
some embedded Intel x86 platforms. This is not needed for PC-type
machines.
bool
config INTEL_SCU
bool
select INTEL_SCU_IPC
config INTEL_SCU_PCI
bool "Intel SCU PCI driver"
depends on PCI
select INTEL_SCU
help
This driver is used to bridge the communications between kernel
and SCU on some embedded Intel x86 platforms. It also creates
devices that are connected to the SoC through the SCU.
Platforms supported:
Medfield
Clovertrail
Merrifield
Broxton
Apollo Lake
config INTEL_SCU_PLATFORM
tristate "Intel SCU platform driver"
depends on ACPI
select INTEL_SCU
help
This driver is used to bridge the communications between kernel
and SCU (sometimes called PMC as well). The driver currently
supports Intel Elkhart Lake and compatible platforms.
config INTEL_SCU_IPC_UTIL
tristate "Intel SCU IPC utility driver"
depends on INTEL_SCU_IPC
depends on INTEL_SCU
---help---
The IPC Util driver provides an interface with the SCU enabling
low level access for debug work and updating the firmware. Say
@@ -1360,7 +1385,9 @@ config INTEL_SCU_IPC_UTIL
config INTEL_TELEMETRY
tristate "Intel SoC Telemetry Driver"
depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
depends on X86_64
depends on MFD_INTEL_PMC_BXT
depends on INTEL_PUNIT_IPC
---help---
This driver provides interfaces to configure and use
telemetry for INTEL SoC from APL onwards. It is also

Melihat File

@@ -11,6 +11,7 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
# WMI drivers
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
obj-$(CONFIG_INTEL_WMI_SBL_FW_UPDATE) += intel-wmi-sbl-fw-update.o
obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
@@ -138,9 +139,10 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o
obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
intel_telemetry_pltdrv.o \

Melihat File

@@ -827,7 +827,7 @@ MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:");
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:");
MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:");
MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:");
module_init(acerhdf_init);
module_exit(acerhdf_exit);

Melihat File

@@ -640,22 +640,15 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
static void asus_led_exit(struct asus_laptop *asus)
{
if (!IS_ERR_OR_NULL(asus->wled.led.dev))
led_classdev_unregister(&asus->wled.led);
if (!IS_ERR_OR_NULL(asus->bled.led.dev))
led_classdev_unregister(&asus->bled.led);
if (!IS_ERR_OR_NULL(asus->mled.led.dev))
led_classdev_unregister(&asus->mled.led);
if (!IS_ERR_OR_NULL(asus->tled.led.dev))
led_classdev_unregister(&asus->tled.led);
if (!IS_ERR_OR_NULL(asus->pled.led.dev))
led_classdev_unregister(&asus->pled.led);
if (!IS_ERR_OR_NULL(asus->rled.led.dev))
led_classdev_unregister(&asus->rled.led);
if (!IS_ERR_OR_NULL(asus->gled.led.dev))
led_classdev_unregister(&asus->gled.led);
if (!IS_ERR_OR_NULL(asus->kled.led.dev))
led_classdev_unregister(&asus->kled.led);
led_classdev_unregister(&asus->wled.led);
led_classdev_unregister(&asus->bled.led);
led_classdev_unregister(&asus->mled.led);
led_classdev_unregister(&asus->tled.led);
led_classdev_unregister(&asus->pled.led);
led_classdev_unregister(&asus->rled.led);
led_classdev_unregister(&asus->gled.led);
led_classdev_unregister(&asus->kled.led);
if (asus->led_workqueue) {
destroy_workqueue(asus->led_workqueue);
asus->led_workqueue = NULL;

Melihat File

@@ -472,6 +472,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */
{ KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */
{ KE_IGNORE, 0x79, }, /* Charger type dectection notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
{ KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */

Melihat File

@@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
#define NOTIFY_BRNDOWN_MIN 0x20
#define NOTIFY_BRNDOWN_MAX 0x2e
#define NOTIFY_FNLOCK_TOGGLE 0x4e
#define NOTIFY_KBD_DOCK_CHANGE 0x75
#define NOTIFY_KBD_BRTUP 0xc4
#define NOTIFY_KBD_BRTDWN 0xc5
#define NOTIFY_KBD_BRTTOGGLE 0xc7
@@ -116,6 +117,8 @@ struct bios_args {
u32 arg0;
u32 arg1;
u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
u32 arg4;
u32 arg5;
} __packed;
/*
@@ -222,45 +225,6 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
};
/* Input **********************************************************************/
static int asus_wmi_input_init(struct asus_wmi *asus)
{
int err;
asus->inputdev = input_allocate_device();
if (!asus->inputdev)
return -ENOMEM;
asus->inputdev->name = asus->driver->input_name;
asus->inputdev->phys = asus->driver->input_phys;
asus->inputdev->id.bustype = BUS_HOST;
asus->inputdev->dev.parent = &asus->platform_device->dev;
set_bit(EV_REP, asus->inputdev->evbit);
err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
if (err)
goto err_free_dev;
err = input_register_device(asus->inputdev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
input_free_device(asus->inputdev);
return err;
}
static void asus_wmi_input_exit(struct asus_wmi *asus)
{
if (asus->inputdev)
input_unregister_device(asus->inputdev);
asus->inputdev = NULL;
}
/* WMI ************************************************************************/
static int asus_wmi_evaluate_method3(u32 method_id,
@@ -309,7 +273,7 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
struct acpi_buffer input;
u64 phys_addr;
u32 retval;
u32 status = -1;
u32 status;
/*
* Copy to dma capable address otherwise memory corruption occurs as
@@ -381,6 +345,53 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
}
/* Input **********************************************************************/
static int asus_wmi_input_init(struct asus_wmi *asus)
{
int err, result;
asus->inputdev = input_allocate_device();
if (!asus->inputdev)
return -ENOMEM;
asus->inputdev->name = asus->driver->input_name;
asus->inputdev->phys = asus->driver->input_phys;
asus->inputdev->id.bustype = BUS_HOST;
asus->inputdev->dev.parent = &asus->platform_device->dev;
set_bit(EV_REP, asus->inputdev->evbit);
err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
if (err)
goto err_free_dev;
result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK);
if (result >= 0) {
input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);
input_report_switch(asus->inputdev, SW_TABLET_MODE, !result);
} else if (result != -ENODEV) {
pr_err("Error checking for keyboard-dock: %d\n", result);
}
err = input_register_device(asus->inputdev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
input_free_device(asus->inputdev);
return err;
}
static void asus_wmi_input_exit(struct asus_wmi *asus)
{
if (asus->inputdev)
input_unregister_device(asus->inputdev);
asus->inputdev = NULL;
}
/* Battery ********************************************************************/
/* The battery maximum charging percentage */
@@ -675,14 +686,11 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
static void asus_wmi_led_exit(struct asus_wmi *asus)
{
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
led_classdev_unregister(&asus->kbd_led);
if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
led_classdev_unregister(&asus->tpd_led);
if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
led_classdev_unregister(&asus->wlan_led);
if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
led_classdev_unregister(&asus->lightbar_led);
led_classdev_unregister(&asus->kbd_led);
led_classdev_unregister(&asus->tpd_led);
led_classdev_unregister(&asus->wlan_led);
led_classdev_unregister(&asus->lightbar_led);
if (asus->led_workqueue)
destroy_workqueue(asus->led_workqueue);
}
@@ -2058,9 +2066,9 @@ static int asus_wmi_get_event_code(u32 value)
static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
{
int orig_code;
unsigned int key_value = 1;
bool autorelease = 1;
int result, orig_code;
orig_code = code;
@@ -2105,6 +2113,17 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
return;
}
if (code == NOTIFY_KBD_DOCK_CHANGE) {
result = asus_wmi_get_devstate_simple(asus,
ASUS_WMI_DEVID_KBD_DOCK);
if (result >= 0) {
input_report_switch(asus->inputdev, SW_TABLET_MODE,
!result);
input_sync(asus->inputdev);
}
return;
}
if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) {
fan_boost_mode_switch_next(asus);
return;

Melihat File

@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/dma-mapping.h>
#include <linux/dmi.h>
#include <linux/errno.h>
#include <linux/cpu.h>
#include <linux/gfp.h>
@@ -34,7 +35,7 @@
#include "dcdbas.h"
#define DRIVER_NAME "dcdbas"
#define DRIVER_VERSION "5.6.0-3.3"
#define DRIVER_VERSION "5.6.0-3.4"
#define DRIVER_DESCRIPTION "Dell Systems Management Base Driver"
static struct platform_device *dcdbas_pdev;
@@ -45,7 +46,7 @@ static unsigned long smi_data_buf_size;
static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
static u32 smi_data_buf_phys_addr;
static DEFINE_MUTEX(smi_data_lock);
static u8 *eps_buffer;
static u8 *bios_buffer;
static unsigned int host_control_action;
static unsigned int host_control_smi_type;
@@ -518,8 +519,10 @@ static inline struct smm_eps_table *check_eps_table(u8 *addr)
static int dcdbas_check_wsmt(void)
{
const struct dmi_device *dev = NULL;
struct acpi_table_wsmt *wsmt = NULL;
struct smm_eps_table *eps = NULL;
u64 bios_buf_paddr;
u64 remap_size;
u8 *addr;
@@ -532,6 +535,17 @@ static int dcdbas_check_wsmt(void)
!(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION))
return 0;
/*
* BIOS could provide the address/size of the protected buffer
* in an SMBIOS string or in an EPS structure in 0xFxxxx.
*/
/* Check SMBIOS for buffer address */
while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev)))
if (sscanf(dev->name, "30[%16llx;%8llx]", &bios_buf_paddr,
&remap_size) == 2)
goto remap;
/* Scan for EPS (entry point structure) */
for (addr = (u8 *)__va(0xf0000);
addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table));
@@ -542,34 +556,37 @@ static int dcdbas_check_wsmt(void)
}
if (!eps) {
dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n");
dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no firmware buffer found\n");
return -ENODEV;
}
bios_buf_paddr = eps->smm_comm_buff_addr;
remap_size = eps->num_of_4k_pages * PAGE_SIZE;
remap:
/*
* Get physical address of buffer and map to virtual address.
* Table gives size in 4K pages, regardless of actual system page size.
*/
if (upper_32_bits(eps->smm_comm_buff_addr + 8)) {
dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n");
if (upper_32_bits(bios_buf_paddr + 8)) {
dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
return -EINVAL;
}
/*
* Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8
* bytes are used for a semaphore, not the data buffer itself).
*/
remap_size = eps->num_of_4k_pages * PAGE_SIZE;
if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8)
remap_size = MAX_SMI_DATA_BUF_SIZE + 8;
eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB);
if (!eps_buffer) {
dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n");
bios_buffer = memremap(bios_buf_paddr, remap_size, MEMREMAP_WB);
if (!bios_buffer) {
dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map buffer\n");
return -ENOMEM;
}
/* First 8 bytes is for a semaphore, not part of the smi_data_buf */
smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8;
smi_data_buf = eps_buffer + 8;
smi_data_buf_phys_addr = bios_buf_paddr + 8;
smi_data_buf = bios_buffer + 8;
smi_data_buf_size = remap_size - 8;
max_smi_data_buf_size = smi_data_buf_size;
wsmt_enabled = true;
@@ -736,8 +753,8 @@ static void __exit dcdbas_exit(void)
*/
if (dcdbas_pdev)
smi_data_buf_free();
if (eps_buffer)
memunmap(eps_buffer);
if (bios_buffer)
memunmap(bios_buffer);
platform_device_unregister(dcdbas_pdev_reg);
platform_driver_unregister(&dcdbas_driver);
}

Melihat File

@@ -2204,10 +2204,13 @@ static int __init dell_init(void)
dell_laptop_register_notifier(&dell_laptop_notifier);
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
if (ret < 0)
goto fail_led;
if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) {
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
if (ret < 0)
goto fail_led;
}
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0;

Melihat File

@@ -310,6 +310,16 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = {
/* Battery inserted */
{ KE_IGNORE, 0xfff1, { KEY_RESERVED } },
/*
* Detachable keyboard detached / undocked
* Note SW_TABLET_MODE is already reported through the intel_vbtn
* driver for this, so we ignore it.
*/
{ KE_IGNORE, 0xfff2, { KEY_RESERVED } },
/* Detachable keyboard attached / docked */
{ KE_IGNORE, 0xfff3, { KEY_RESERVED } },
/* Keyboard backlight level changed */
{ KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } },
{ KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } },

Melihat File

@@ -541,13 +541,11 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
static void eeepc_led_exit(struct eeepc_laptop *eeepc)
{
if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
led_classdev_unregister(&eeepc->tpd_led);
led_classdev_unregister(&eeepc->tpd_led);
if (eeepc->led_workqueue)
destroy_workqueue(eeepc->led_workqueue);
}
/*
* PCI hotplug (for wlan rfkill)
*/

Melihat File

@@ -111,10 +111,10 @@ enum hp_wireless2_bits {
HPWMI_POWER_SOFT = 0x02,
HPWMI_POWER_BIOS = 0x04,
HPWMI_POWER_HARD = 0x08,
HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
};
#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
!= (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
struct bios_rfkill2_device_state {
@@ -461,8 +461,14 @@ static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
static ssize_t als_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u32 tmp = simple_strtoul(buf, NULL, 10);
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
u32 tmp;
int ret;
ret = kstrtou32(buf, 10, &tmp);
if (ret)
return ret;
ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
sizeof(tmp), sizeof(tmp));
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -473,22 +479,20 @@ static ssize_t als_store(struct device *dev, struct device_attribute *attr,
static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
long unsigned int tmp2;
u32 tmp = 1;
bool clear;
int ret;
u32 tmp;
ret = kstrtoul(buf, 10, &tmp2);
if (!ret && tmp2 != 1)
ret = -EINVAL;
ret = kstrtobool(buf, &clear);
if (ret)
goto out;
return ret;
if (clear == false)
return -EINVAL;
/* Clear the POST error code. It is kept until until cleared. */
tmp = (u32) tmp2;
ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
sizeof(tmp), sizeof(tmp));
out:
if (ret)
return ret < 0 ? ret : -EINVAL;

Melihat File

@@ -79,6 +79,13 @@ static const struct dmi_system_id button_array_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 16"),
},
},
{
.ident = "HP Spectre x2 (2015)",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x2 Detachable"),
},
},
{ }
};

Melihat File

@@ -40,28 +40,70 @@ static const struct key_entry intel_vbtn_keymap[] = {
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
{ KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key press */
{ KE_KEY, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key release */
};
static const struct key_entry intel_vbtn_switchmap[] = {
{ KE_SW, 0xCA, { .sw = { SW_DOCK, 1 } } }, /* Docked */
{ KE_SW, 0xCB, { .sw = { SW_DOCK, 0 } } }, /* Undocked */
{ KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */
{ KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */
{ KE_END },
};
#define KEYMAP_LEN \
(ARRAY_SIZE(intel_vbtn_keymap) + ARRAY_SIZE(intel_vbtn_switchmap) + 1)
struct intel_vbtn_priv {
struct key_entry keymap[KEYMAP_LEN];
struct input_dev *input_dev;
bool has_buttons;
bool has_switches;
bool wakeup_mode;
};
static void detect_tablet_mode(struct platform_device *device)
{
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
acpi_handle handle = ACPI_HANDLE(&device->dev);
unsigned long long vgbs;
acpi_status status;
int m;
status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
if (ACPI_FAILURE(status))
return;
m = !(vgbs & TABLET_MODE_FLAG);
input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
m = (vgbs & DOCK_MODE_FLAG) ? 1 : 0;
input_report_switch(priv->input_dev, SW_DOCK, m);
}
static int intel_vbtn_input_setup(struct platform_device *device)
{
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
int ret;
int ret, keymap_len = 0;
if (priv->has_buttons) {
memcpy(&priv->keymap[keymap_len], intel_vbtn_keymap,
ARRAY_SIZE(intel_vbtn_keymap) *
sizeof(struct key_entry));
keymap_len += ARRAY_SIZE(intel_vbtn_keymap);
}
if (priv->has_switches) {
memcpy(&priv->keymap[keymap_len], intel_vbtn_switchmap,
ARRAY_SIZE(intel_vbtn_switchmap) *
sizeof(struct key_entry));
keymap_len += ARRAY_SIZE(intel_vbtn_switchmap);
}
priv->keymap[keymap_len].type = KE_END;
priv->input_dev = devm_input_allocate_device(&device->dev);
if (!priv->input_dev)
return -ENOMEM;
ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
ret = sparse_keymap_setup(priv->input_dev, priv->keymap, NULL);
if (ret)
return ret;
@@ -69,6 +111,9 @@ static int intel_vbtn_input_setup(struct platform_device *device)
priv->input_dev->name = "Intel Virtual Button driver";
priv->input_dev->id.bustype = BUS_HOST;
if (priv->has_switches)
detect_tablet_mode(device);
return input_register_device(priv->input_dev);
}
@@ -114,44 +159,46 @@ out_unknown:
dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
}
static void detect_tablet_mode(struct platform_device *device)
static bool intel_vbtn_has_buttons(acpi_handle handle)
{
acpi_status status;
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
return ACPI_SUCCESS(status);
}
static bool intel_vbtn_has_switches(acpi_handle handle)
{
const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
acpi_handle handle = ACPI_HANDLE(&device->dev);
struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
unsigned long long vgbs;
acpi_status status;
int m;
if (!(chassis_type && strcmp(chassis_type, "31") == 0))
goto out;
/*
* Some normal laptops have a VGBS method despite being non-convertible
* and their VGBS method always returns 0, causing detect_tablet_mode()
* to report SW_TABLET_MODE=1 to userspace, which causes issues.
* These laptops have a DMI chassis_type of 9 ("Laptop"), do not report
* switches on any devices with a DMI chassis_type of 9.
*/
if (chassis_type && strcmp(chassis_type, "9") == 0)
return false;
status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
if (ACPI_FAILURE(status))
goto out;
obj = vgbs_output.pointer;
if (!(obj && obj->type == ACPI_TYPE_INTEGER))
goto out;
m = !(obj->integer.value & TABLET_MODE_FLAG);
input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
m = (obj->integer.value & DOCK_MODE_FLAG) ? 1 : 0;
input_report_switch(priv->input_dev, SW_DOCK, m);
out:
kfree(vgbs_output.pointer);
status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
return ACPI_SUCCESS(status);
}
static int intel_vbtn_probe(struct platform_device *device)
{
acpi_handle handle = ACPI_HANDLE(&device->dev);
bool has_buttons, has_switches;
struct intel_vbtn_priv *priv;
acpi_status status;
int err;
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
if (ACPI_FAILURE(status)) {
has_buttons = intel_vbtn_has_buttons(handle);
has_switches = intel_vbtn_has_switches(handle);
if (!has_buttons && !has_switches) {
dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
return -ENODEV;
}
@@ -161,14 +208,15 @@ static int intel_vbtn_probe(struct platform_device *device)
return -ENOMEM;
dev_set_drvdata(&device->dev, priv);
priv->has_buttons = has_buttons;
priv->has_switches = has_switches;
err = intel_vbtn_input_setup(device);
if (err) {
pr_err("Failed to setup Intel Virtual Button\n");
return err;
}
detect_tablet_mode(device);
status = acpi_install_notify_handler(handle,
ACPI_DEVICE_NOTIFY,
notify_handler,

Melihat File

@@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Slim Bootloader(SBL) firmware update signaling driver
*
* Slim Bootloader is a small, open-source, non UEFI compliant, boot firmware
* optimized for running on certain Intel platforms.
*
* SBL exposes an ACPI-WMI device via /sys/bus/wmi/devices/<INTEL_WMI_SBL_GUID>.
* This driver further adds "firmware_update_request" device attribute.
* This attribute normally has a value of 0 and userspace can signal SBL
* to update firmware, on next reboot, by writing a value of 1.
*
* More details of SBL firmware update process is available at:
* https://slimbootloader.github.io/security/firmware-update.html
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/wmi.h>
#define INTEL_WMI_SBL_GUID "44FADEB1-B204-40F2-8581-394BBDC1B651"
static int get_fwu_request(struct device *dev, u32 *out)
{
struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
acpi_status status;
status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result);
if (ACPI_FAILURE(status)) {
dev_err(dev, "wmi_query_block failed\n");
return -ENODEV;
}
obj = (union acpi_object *)result.pointer;
if (!obj || obj->type != ACPI_TYPE_INTEGER) {
dev_warn(dev, "wmi_query_block returned invalid value\n");
kfree(obj);
return -EINVAL;
}
*out = obj->integer.value;
kfree(obj);
return 0;
}
static int set_fwu_request(struct device *dev, u32 in)
{
struct acpi_buffer input;
acpi_status status;
u32 value;
value = in;
input.length = sizeof(u32);
input.pointer = &value;
status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input);
if (ACPI_FAILURE(status)) {
dev_err(dev, "wmi_set_block failed\n");
return -ENODEV;
}
return 0;
}
static ssize_t firmware_update_request_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u32 val;
int ret;
ret = get_fwu_request(dev, &val);
if (ret)
return ret;
return sprintf(buf, "%d\n", val);
}
static ssize_t firmware_update_request_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int val;
int ret;
ret = kstrtouint(buf, 0, &val);
if (ret)
return ret;
/* May later be extended to support values other than 0 and 1 */
if (val > 1)
return -ERANGE;
ret = set_fwu_request(dev, val);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(firmware_update_request);
static struct attribute *firmware_update_attrs[] = {
&dev_attr_firmware_update_request.attr,
NULL
};
ATTRIBUTE_GROUPS(firmware_update);
static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev,
const void *context)
{
dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n");
return 0;
}
static int intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
{
dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n");
return 0;
}
static const struct wmi_device_id intel_wmi_sbl_id_table[] = {
{ .guid_string = INTEL_WMI_SBL_GUID },
{}
};
MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table);
static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
.driver = {
.name = "intel-wmi-sbl-fw-update",
.dev_groups = firmware_update_groups,
},
.probe = intel_wmi_sbl_fw_update_probe,
.remove = intel_wmi_sbl_fw_update_remove,
.id_table = intel_wmi_sbl_id_table,
};
module_wmi_driver(intel_wmi_sbl_fw_update_driver);
MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>");
MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -6,14 +6,14 @@
*
* Some Intel Cherry Trail based device which ship with Windows 10, have
* this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2
* resources, for 4 different chips attached to various i2c busses:
* 1. The Whiskey Cove pmic, which is also described by the INT34D3 ACPI device
* resources, for 4 different chips attached to various I²C buses:
* 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device
* 2. Maxim MAX17047 Fuel Gauge Controller
* 3. FUSB302 USB Type-C Controller
* 4. PI3USB30532 USB switch
*
* So this driver is a stub / pseudo driver whose only purpose is to
* instantiate i2c-clients for chips 2 - 4, so that standard i2c drivers
* instantiate I²C clients for chips 2 - 4, so that standard I²C drivers
* for these chips can bind to the them.
*/
@@ -21,43 +21,32 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/usb/pd.h>
#include "intel_cht_int33fe_common.h"
enum {
INT33FE_NODE_FUSB302,
INT33FE_NODE_MAX17047,
INT33FE_NODE_PI3USB30532,
INT33FE_NODE_DISPLAYPORT,
INT33FE_NODE_USB_CONNECTOR,
INT33FE_NODE_MAX,
};
/*
* Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates
* Grrr, I severely dislike buggy BIOS-es. At least one BIOS enumerates
* the max17047 both through the INT33FE ACPI device (it is right there
* in the resources table) as well as through a separate MAX17047 device.
*
* These helpers are used to work around this by checking if an i2c-client
* These helpers are used to work around this by checking if an I²C client
* for the max17047 has already been registered.
*/
static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
{
struct i2c_client **max17047 = data;
struct acpi_device *adev;
const char *hid;
adev = ACPI_COMPANION(dev);
if (!adev)
return 0;
hid = acpi_device_hid(adev);
/* The MAX17047 ACPI node doesn't have an UID, so we don't check that */
if (strcmp(hid, "MAX17047"))
if (!acpi_dev_hid_uid_match(adev, "MAX17047", NULL))
return 0;
*max17047 = to_i2c_client(dev);
@@ -66,11 +55,16 @@ static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
static const char * const max17047_suppliers[] = { "bq24190-charger" };
static const struct property_entry max17047_props[] = {
static const struct property_entry max17047_properties[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers),
{ }
};
static const struct software_node max17047_node = {
.name = "max17047",
.properties = max17047_properties,
};
/*
* We are not using inline property here because those are constant,
* and we need to adjust this one at runtime to point to real
@@ -80,12 +74,17 @@ static struct software_node_ref_args fusb302_mux_refs[] = {
{ .node = NULL },
};
static const struct property_entry fusb302_props[] = {
static const struct property_entry fusb302_properties[] = {
PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
PROPERTY_ENTRY_REF_ARRAY("usb-role-switch", fusb302_mux_refs),
{ }
};
static const struct software_node fusb302_node = {
.name = "fusb302",
.properties = fusb302_properties,
};
#define PDO_FIXED_FLAGS \
(PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
@@ -98,31 +97,40 @@ static const u32 snk_pdo[] = {
PDO_VAR(5000, 12000, 3000),
};
static const struct software_node nodes[];
static const struct software_node pi3usb30532_node = {
.name = "pi3usb30532",
};
static const struct property_entry usb_connector_props[] = {
static const struct software_node displayport_node = {
.name = "displayport",
};
static const struct property_entry usb_connector_properties[] = {
PROPERTY_ENTRY_STRING("data-role", "dual"),
PROPERTY_ENTRY_STRING("power-role", "dual"),
PROPERTY_ENTRY_STRING("try-power-role", "sink"),
PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000),
PROPERTY_ENTRY_REF("orientation-switch",
&nodes[INT33FE_NODE_PI3USB30532]),
PROPERTY_ENTRY_REF("mode-switch",
&nodes[INT33FE_NODE_PI3USB30532]),
PROPERTY_ENTRY_REF("displayport",
&nodes[INT33FE_NODE_DISPLAYPORT]),
PROPERTY_ENTRY_REF("orientation-switch", &pi3usb30532_node),
PROPERTY_ENTRY_REF("mode-switch", &pi3usb30532_node),
PROPERTY_ENTRY_REF("displayport", &displayport_node),
{ }
};
static const struct software_node nodes[] = {
{ "fusb302", NULL, fusb302_props },
{ "max17047", NULL, max17047_props },
{ "pi3usb30532" },
{ "displayport" },
{ "connector", &nodes[0], usb_connector_props },
{ }
static const struct software_node usb_connector_node = {
.name = "connector",
.parent = &fusb302_node,
.properties = usb_connector_properties,
};
static const struct software_node *node_group[] = {
&fusb302_node,
&max17047_node,
&pi3usb30532_node,
&displayport_node,
&usb_connector_node,
NULL
};
static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
@@ -130,7 +138,7 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
struct fwnode_handle *fwnode;
struct pci_dev *pdev;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_DISPLAYPORT]);
fwnode = software_node_fwnode(&displayport_node);
if (!fwnode)
return -ENODEV;
@@ -155,11 +163,10 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
{
software_node_unregister_nodes(nodes);
software_node_unregister_node_group(node_group);
if (fusb302_mux_refs[0].node) {
fwnode_handle_put(
software_node_fwnode(fusb302_mux_refs[0].node));
fwnode_handle_put(software_node_fwnode(fusb302_mux_refs[0].node));
fusb302_mux_refs[0].node = NULL;
}
@@ -192,7 +199,7 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)
*/
fusb302_mux_refs[0].node = mux_ref_node;
ret = software_node_register_nodes(nodes);
ret = software_node_register_node_group(node_group);
if (ret)
return ret;
@@ -222,16 +229,15 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
struct fwnode_handle *fwnode;
int ret;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_MAX17047]);
fwnode = software_node_fwnode(&max17047_node);
if (!fwnode)
return -ENODEV;
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
if (max17047) {
/* Pre-existing i2c-client for the max17047, add device-props */
fwnode->secondary = ERR_PTR(-ENODEV);
max17047->dev.fwnode->secondary = fwnode;
/* And re-probe to get the new device-props applied. */
/* Pre-existing I²C client for the max17047, add device properties */
set_secondary_fwnode(&max17047->dev, fwnode);
/* And re-probe to get the new device properties applied */
ret = device_reprobe(&max17047->dev);
if (ret)
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
@@ -266,7 +272,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
* must be registered before the fusb302 is instantiated, otherwise
* it will end up with a dummy-regulator.
* Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data
* which is defined in i2c-cht-wc.c from where the bq24292i i2c-client
* which is defined in i2c-cht-wc.c from where the bq24292i I²C client
* gets instantiated. We use regulator_get_optional here so that we
* don't end up getting a dummy-regulator ourselves.
*/
@@ -277,7 +283,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
}
regulator_put(regulator);
/* The FUSB302 uses the irq at index 1 and is the only irq user */
/* The FUSB302 uses the IRQ at index 1 and is the only IRQ user */
fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
if (fusb302_irq < 0) {
if (fusb302_irq != -EPROBE_DEFER)
@@ -289,12 +295,12 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
if (ret)
return ret;
/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */
/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047() */
ret = cht_int33fe_register_max17047(dev, data);
if (ret)
goto out_remove_nodes;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]);
fwnode = software_node_fwnode(&fusb302_node);
if (!fwnode) {
ret = -ENODEV;
goto out_unregister_max17047;
@@ -312,7 +318,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
goto out_unregister_max17047;
}
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_PI3USB30532]);
fwnode = software_node_fwnode(&pi3usb30532_node);
if (!fwnode) {
ret = -ENODEV;
goto out_unregister_fusb302;

Melihat File

@@ -46,6 +46,7 @@ struct mid_pb_ddata {
unsigned short mirqlvl1_addr;
unsigned short pbstat_addr;
u8 pbstat_mask;
struct intel_scu_ipc_dev *scu;
int (*setup)(struct mid_pb_ddata *ddata);
};
@@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
int ret;
u8 pbstat;
ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
&pbstat);
if (ret)
return ret;
@@ -67,14 +69,15 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
static int mid_irq_ack(struct mid_pb_ddata *ddata)
{
return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
MSIC_PWRBTNM);
}
static int mrfld_setup(struct mid_pb_ddata *ddata)
{
/* Unmask the PBIRQ and MPBIRQ on Tangier */
intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
return 0;
}
@@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev)
return error;
}
ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
if (!ddata->scu)
return -EPROBE_DEFER;
error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
IRQF_ONESHOT, DRIVER_NAME, ddata);
if (error) {

Melihat File

@@ -1,949 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the Intel PMC IPC mechanism
*
* (C) Copyright 2014-2015 Intel Corporation
*
* This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by
* Sreedhara DS <sreedhara.ds@intel.com>
*
* PMC running in ARC processor communicates with other entity running in IA
* core through IPC mechanism which in turn messaging between IA core ad PMC.
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <asm/intel_pmc_ipc.h>
#include <linux/platform_data/itco_wdt.h>
/*
* IPC registers
* The IA write to IPC_CMD command register triggers an interrupt to the ARC,
* The ARC handles the interrupt and services it, writing optional data to
* the IPC1 registers, updates the IPC_STS response register with the status.
*/
#define IPC_CMD 0x00
#define IPC_CMD_MSI BIT(8)
#define IPC_CMD_SIZE 16
#define IPC_CMD_SUBCMD 12
#define IPC_STATUS 0x04
#define IPC_STATUS_IRQ BIT(2)
#define IPC_STATUS_ERR BIT(1)
#define IPC_STATUS_BUSY BIT(0)
#define IPC_SPTR 0x08
#define IPC_DPTR 0x0C
#define IPC_WRITE_BUFFER 0x80
#define IPC_READ_BUFFER 0x90
/* Residency with clock rate at 19.2MHz to usecs */
#define S0IX_RESIDENCY_IN_USECS(d, s) \
({ \
u64 result = 10ull * ((d) + (s)); \
do_div(result, 192); \
result; \
})
/*
* 16-byte buffer for sending data associated with IPC command.
*/
#define IPC_DATA_BUFFER_SIZE 16
#define IPC_LOOP_CNT 3000000
#define IPC_MAX_SEC 3
#define IPC_TRIGGER_MODE_IRQ true
/* exported resources from IFWI */
#define PLAT_RESOURCE_IPC_INDEX 0
#define PLAT_RESOURCE_IPC_SIZE 0x1000
#define PLAT_RESOURCE_GCR_OFFSET 0x1000
#define PLAT_RESOURCE_GCR_SIZE 0x1000
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
#define PLAT_RESOURCE_ISP_DATA_INDEX 4
#define PLAT_RESOURCE_ISP_IFACE_INDEX 5
#define PLAT_RESOURCE_GTD_DATA_INDEX 6
#define PLAT_RESOURCE_GTD_IFACE_INDEX 7
#define PLAT_RESOURCE_ACPI_IO_INDEX 0
/*
* BIOS does not create an ACPI device for each PMC function,
* but exports multiple resources from one ACPI device(IPC) for
* multiple functions. This driver is responsible to create a
* platform device and to export resources for those functions.
*/
#define TCO_DEVICE_NAME "iTCO_wdt"
#define SMI_EN_OFFSET 0x40
#define SMI_EN_SIZE 4
#define TCO_BASE_OFFSET 0x60
#define TCO_REGS_SIZE 16
#define PUNIT_DEVICE_NAME "intel_punit_ipc"
#define TELEMETRY_DEVICE_NAME "intel_telemetry"
#define TELEM_SSRAM_SIZE 240
#define TELEM_PMC_SSRAM_OFFSET 0x1B00
#define TELEM_PUNIT_SSRAM_OFFSET 0x1A00
#define TCO_PMC_OFFSET 0x08
#define TCO_PMC_SIZE 0x04
/* PMC register bit definitions */
/* PMC_CFG_REG bit masks */
#define PMC_CFG_NO_REBOOT_MASK BIT_MASK(4)
#define PMC_CFG_NO_REBOOT_EN (1 << 4)
#define PMC_CFG_NO_REBOOT_DIS (0 << 4)
static struct intel_pmc_ipc_dev {
struct device *dev;
void __iomem *ipc_base;
bool irq_mode;
int irq;
int cmd;
struct completion cmd_complete;
/* The following PMC BARs share the same ACPI device with the IPC */
resource_size_t acpi_io_base;
int acpi_io_size;
struct platform_device *tco_dev;
/* gcr */
void __iomem *gcr_mem_base;
bool has_gcr_regs;
spinlock_t gcr_lock;
/* punit */
struct platform_device *punit_dev;
unsigned int punit_res_count;
/* Telemetry */
resource_size_t telem_pmc_ssram_base;
resource_size_t telem_punit_ssram_base;
int telem_pmc_ssram_size;
int telem_punit_ssram_size;
u8 telem_res_inval;
struct platform_device *telemetry_dev;
} ipcdev;
static char *ipc_err_sources[] = {
[IPC_ERR_NONE] =
"no error",
[IPC_ERR_CMD_NOT_SUPPORTED] =
"command not supported",
[IPC_ERR_CMD_NOT_SERVICED] =
"command not serviced",
[IPC_ERR_UNABLE_TO_SERVICE] =
"unable to service",
[IPC_ERR_CMD_INVALID] =
"command invalid",
[IPC_ERR_CMD_FAILED] =
"command failed",
[IPC_ERR_EMSECURITY] =
"Invalid Battery",
[IPC_ERR_UNSIGNEDKERNEL] =
"Unsigned kernel",
};
/* Prevent concurrent calls to the PMC */
static DEFINE_MUTEX(ipclock);
static inline void ipc_send_command(u32 cmd)
{
ipcdev.cmd = cmd;
if (ipcdev.irq_mode) {
reinit_completion(&ipcdev.cmd_complete);
cmd |= IPC_CMD_MSI;
}
writel(cmd, ipcdev.ipc_base + IPC_CMD);
}
static inline u32 ipc_read_status(void)
{
return readl(ipcdev.ipc_base + IPC_STATUS);
}
static inline void ipc_data_writel(u32 data, u32 offset)
{
writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
}
static inline u32 ipc_data_readl(u32 offset)
{
return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
}
static inline u64 gcr_data_readq(u32 offset)
{
return readq(ipcdev.gcr_mem_base + offset);
}
static inline int is_gcr_valid(u32 offset)
{
if (!ipcdev.has_gcr_regs)
return -EACCES;
if (offset > PLAT_RESOURCE_GCR_SIZE)
return -EINVAL;
return 0;
}
/**
* intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
* @offset: offset of GCR register from GCR address base
* @data: data pointer for storing the register output
*
* Reads the 64-bit PMC GCR register at given offset.
*
* Return: negative value on error or 0 on success.
*/
int intel_pmc_gcr_read64(u32 offset, u64 *data)
{
int ret;
spin_lock(&ipcdev.gcr_lock);
ret = is_gcr_valid(offset);
if (ret < 0) {
spin_unlock(&ipcdev.gcr_lock);
return ret;
}
*data = readq(ipcdev.gcr_mem_base + offset);
spin_unlock(&ipcdev.gcr_lock);
return 0;
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
/**
* intel_pmc_gcr_update() - Update PMC GCR register bits
* @offset: offset of GCR register from GCR address base
* @mask: bit mask for update operation
* @val: update value
*
* Updates the bits of given GCR register as specified by
* @mask and @val.
*
* Return: negative value on error or 0 on success.
*/
static int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
{
u32 new_val;
int ret = 0;
spin_lock(&ipcdev.gcr_lock);
ret = is_gcr_valid(offset);
if (ret < 0)
goto gcr_ipc_unlock;
new_val = readl(ipcdev.gcr_mem_base + offset);
new_val &= ~mask;
new_val |= val & mask;
writel(new_val, ipcdev.gcr_mem_base + offset);
new_val = readl(ipcdev.gcr_mem_base + offset);
/* check whether the bit update is successful */
if ((new_val & mask) != (val & mask)) {
ret = -EIO;
goto gcr_ipc_unlock;
}
gcr_ipc_unlock:
spin_unlock(&ipcdev.gcr_lock);
return ret;
}
static int update_no_reboot_bit(void *priv, bool set)
{
u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS;
return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG,
PMC_CFG_NO_REBOOT_MASK, value);
}
static int intel_pmc_ipc_check_status(void)
{
int status;
int ret = 0;
if (ipcdev.irq_mode) {
if (0 == wait_for_completion_timeout(
&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
ret = -ETIMEDOUT;
} else {
int loop_count = IPC_LOOP_CNT;
while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
udelay(1);
if (loop_count == 0)
ret = -ETIMEDOUT;
}
status = ipc_read_status();
if (ret == -ETIMEDOUT) {
dev_err(ipcdev.dev,
"IPC timed out, TS=0x%x, CMD=0x%x\n",
status, ipcdev.cmd);
return ret;
}
if (status & IPC_STATUS_ERR) {
int i;
ret = -EIO;
i = (status >> IPC_CMD_SIZE) & 0xFF;
if (i < ARRAY_SIZE(ipc_err_sources))
dev_err(ipcdev.dev,
"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
ipc_err_sources[i], status, ipcdev.cmd);
else
dev_err(ipcdev.dev,
"IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
status, ipcdev.cmd);
if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
ret = -EACCES;
}
return ret;
}
/**
* intel_pmc_ipc_simple_command() - Simple IPC command
* @cmd: IPC command code.
* @sub: IPC command sub type.
*
* Send a simple IPC command to PMC when don't need to specify
* input/output data and source/dest pointers.
*
* Return: an IPC error code or 0 on success.
*/
static int intel_pmc_ipc_simple_command(int cmd, int sub)
{
int ret;
mutex_lock(&ipclock);
if (ipcdev.dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
ret = intel_pmc_ipc_check_status();
mutex_unlock(&ipclock);
return ret;
}
/**
* intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
* @cmd: IPC command code.
* @sub: IPC command sub type.
* @in: input data of this IPC command.
* @inlen: input data length in bytes.
* @out: output data of this IPC command.
* @outlen: output data length in dwords.
* @sptr: data writing to SPTR register.
* @dptr: data writing to DPTR register.
*
* Send an IPC command to PMC with input/output data and source/dest pointers.
*
* Return: an IPC error code or 0 on success.
*/
static int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
u32 outlen, u32 dptr, u32 sptr)
{
u32 wbuf[4] = { 0 };
int ret;
int i;
if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
return -EINVAL;
mutex_lock(&ipclock);
if (ipcdev.dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
memcpy(wbuf, in, inlen);
writel(dptr, ipcdev.ipc_base + IPC_DPTR);
writel(sptr, ipcdev.ipc_base + IPC_SPTR);
/* The input data register is 32bit register and inlen is in Byte */
for (i = 0; i < ((inlen + 3) / 4); i++)
ipc_data_writel(wbuf[i], 4 * i);
ipc_send_command((inlen << IPC_CMD_SIZE) |
(sub << IPC_CMD_SUBCMD) | cmd);
ret = intel_pmc_ipc_check_status();
if (!ret) {
/* out is read from 32bit register and outlen is in 32bit */
for (i = 0; i < outlen; i++)
*out++ = ipc_data_readl(4 * i);
}
mutex_unlock(&ipclock);
return ret;
}
/**
* intel_pmc_ipc_command() - IPC command with input/output data
* @cmd: IPC command code.
* @sub: IPC command sub type.
* @in: input data of this IPC command.
* @inlen: input data length in bytes.
* @out: output data of this IPC command.
* @outlen: output data length in dwords.
*
* Send an IPC command to PMC with input/output data.
*
* Return: an IPC error code or 0 on success.
*/
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
u32 *out, u32 outlen)
{
return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
}
EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
static irqreturn_t ioc(int irq, void *dev_id)
{
int status;
if (ipcdev.irq_mode) {
status = ipc_read_status();
writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
}
complete(&ipcdev.cmd_complete);
return IRQ_HANDLED;
}
static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct intel_pmc_ipc_dev *pmc = &ipcdev;
int ret;
/* Only one PMC is supported */
if (pmc->dev)
return -EBUSY;
pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
spin_lock_init(&ipcdev.gcr_lock);
ret = pcim_enable_device(pdev);
if (ret)
return ret;
ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
if (ret)
return ret;
init_completion(&pmc->cmd_complete);
pmc->ipc_base = pcim_iomap_table(pdev)[0];
ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
pmc);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq\n");
return ret;
}
pmc->dev = &pdev->dev;
pci_set_drvdata(pdev, pmc);
return 0;
}
static const struct pci_device_id ipc_pci_ids[] = {
{PCI_VDEVICE(INTEL, 0x0a94), 0},
{PCI_VDEVICE(INTEL, 0x1a94), 0},
{PCI_VDEVICE(INTEL, 0x5a94), 0},
{ 0,}
};
MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
static struct pci_driver ipc_pci_driver = {
.name = "intel_pmc_ipc",
.id_table = ipc_pci_ids,
.probe = ipc_pci_probe,
};
static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int subcmd;
int cmd;
int ret;
ret = sscanf(buf, "%d %d", &cmd, &subcmd);
if (ret != 2) {
dev_err(dev, "Error args\n");
return -EINVAL;
}
ret = intel_pmc_ipc_simple_command(cmd, subcmd);
if (ret) {
dev_err(dev, "command %d error with %d\n", cmd, ret);
return ret;
}
return (ssize_t)count;
}
static DEVICE_ATTR(simplecmd, 0200, NULL, intel_pmc_ipc_simple_cmd_store);
static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
int subcmd;
int ret;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
if (val)
subcmd = 1;
else
subcmd = 0;
ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
if (ret) {
dev_err(dev, "command north %d error with %d\n", subcmd, ret);
return ret;
}
return (ssize_t)count;
}
static DEVICE_ATTR(northpeak, 0200, NULL, intel_pmc_ipc_northpeak_store);
static struct attribute *intel_ipc_attrs[] = {
&dev_attr_northpeak.attr,
&dev_attr_simplecmd.attr,
NULL
};
static const struct attribute_group intel_ipc_group = {
.attrs = intel_ipc_attrs,
};
static const struct attribute_group *intel_ipc_groups[] = {
&intel_ipc_group,
NULL
};
static struct resource punit_res_array[] = {
/* Punit BIOS */
{
.flags = IORESOURCE_MEM,
},
{
.flags = IORESOURCE_MEM,
},
/* Punit ISP */
{
.flags = IORESOURCE_MEM,
},
{
.flags = IORESOURCE_MEM,
},
/* Punit GTD */
{
.flags = IORESOURCE_MEM,
},
{
.flags = IORESOURCE_MEM,
},
};
#define TCO_RESOURCE_ACPI_IO 0
#define TCO_RESOURCE_SMI_EN_IO 1
#define TCO_RESOURCE_GCR_MEM 2
static struct resource tco_res[] = {
/* ACPI - TCO */
{
.flags = IORESOURCE_IO,
},
/* ACPI - SMI */
{
.flags = IORESOURCE_IO,
},
};
static struct itco_wdt_platform_data tco_info = {
.name = "Apollo Lake SoC",
.version = 5,
.no_reboot_priv = &ipcdev,
.update_no_reboot_bit = update_no_reboot_bit,
};
#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
#define TELEMETRY_RESOURCE_PMC_SSRAM 1
static struct resource telemetry_res[] = {
/*Telemetry*/
{
.flags = IORESOURCE_MEM,
},
{
.flags = IORESOURCE_MEM,
},
};
static int ipc_create_punit_device(void)
{
struct platform_device *pdev;
const struct platform_device_info pdevinfo = {
.parent = ipcdev.dev,
.name = PUNIT_DEVICE_NAME,
.id = -1,
.res = punit_res_array,
.num_res = ipcdev.punit_res_count,
};
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ipcdev.punit_dev = pdev;
return 0;
}
static int ipc_create_tco_device(void)
{
struct platform_device *pdev;
struct resource *res;
const struct platform_device_info pdevinfo = {
.parent = ipcdev.dev,
.name = TCO_DEVICE_NAME,
.id = -1,
.res = tco_res,
.num_res = ARRAY_SIZE(tco_res),
.data = &tco_info,
.size_data = sizeof(tco_info),
};
res = tco_res + TCO_RESOURCE_ACPI_IO;
res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
res->end = res->start + TCO_REGS_SIZE - 1;
res = tco_res + TCO_RESOURCE_SMI_EN_IO;
res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
res->end = res->start + SMI_EN_SIZE - 1;
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ipcdev.tco_dev = pdev;
return 0;
}
static int ipc_create_telemetry_device(void)
{
struct platform_device *pdev;
struct resource *res;
const struct platform_device_info pdevinfo = {
.parent = ipcdev.dev,
.name = TELEMETRY_DEVICE_NAME,
.id = -1,
.res = telemetry_res,
.num_res = ARRAY_SIZE(telemetry_res),
};
res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
res->start = ipcdev.telem_punit_ssram_base;
res->end = res->start + ipcdev.telem_punit_ssram_size - 1;
res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM;
res->start = ipcdev.telem_pmc_ssram_base;
res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ipcdev.telemetry_dev = pdev;
return 0;
}
static int ipc_create_pmc_devices(void)
{
int ret;
/* If we have ACPI based watchdog use that instead */
if (!acpi_has_watchdog()) {
ret = ipc_create_tco_device();
if (ret) {
dev_err(ipcdev.dev, "Failed to add tco platform device\n");
return ret;
}
}
ret = ipc_create_punit_device();
if (ret) {
dev_err(ipcdev.dev, "Failed to add punit platform device\n");
platform_device_unregister(ipcdev.tco_dev);
return ret;
}
if (!ipcdev.telem_res_inval) {
ret = ipc_create_telemetry_device();
if (ret) {
dev_warn(ipcdev.dev,
"Failed to add telemetry platform device\n");
platform_device_unregister(ipcdev.punit_dev);
platform_device_unregister(ipcdev.tco_dev);
}
}
return ret;
}
static int ipc_plat_get_res(struct platform_device *pdev)
{
struct resource *res, *punit_res = punit_res_array;
void __iomem *addr;
int size;
res = platform_get_resource(pdev, IORESOURCE_IO,
PLAT_RESOURCE_ACPI_IO_INDEX);
if (!res) {
dev_err(&pdev->dev, "Failed to get io resource\n");
return -ENXIO;
}
size = resource_size(res);
ipcdev.acpi_io_base = res->start;
ipcdev.acpi_io_size = size;
dev_info(&pdev->dev, "io res: %pR\n", res);
ipcdev.punit_res_count = 0;
/* This is index 0 to cover BIOS data register */
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_BIOS_DATA_INDEX);
if (!res) {
dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
return -ENXIO;
}
punit_res[ipcdev.punit_res_count++] = *res;
dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
/* This is index 1 to cover BIOS interface register */
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_BIOS_IFACE_INDEX);
if (!res) {
dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
return -ENXIO;
}
punit_res[ipcdev.punit_res_count++] = *res;
dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
/* This is index 2 to cover ISP data register, optional */
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_ISP_DATA_INDEX);
if (res) {
punit_res[ipcdev.punit_res_count++] = *res;
dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
}
/* This is index 3 to cover ISP interface register, optional */
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_ISP_IFACE_INDEX);
if (res) {
punit_res[ipcdev.punit_res_count++] = *res;
dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
}
/* This is index 4 to cover GTD data register, optional */
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_GTD_DATA_INDEX);
if (res) {
punit_res[ipcdev.punit_res_count++] = *res;
dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
}
/* This is index 5 to cover GTD interface register, optional */
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_GTD_IFACE_INDEX);
if (res) {
punit_res[ipcdev.punit_res_count++] = *res;
dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_IPC_INDEX);
if (!res) {
dev_err(&pdev->dev, "Failed to get ipc resource\n");
return -ENXIO;
}
size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
res->end = res->start + size - 1;
addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(addr))
return PTR_ERR(addr);
ipcdev.ipc_base = addr;
ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
dev_info(&pdev->dev, "ipc res: %pR\n", res);
ipcdev.telem_res_inval = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM,
PLAT_RESOURCE_TELEM_SSRAM_INDEX);
if (!res) {
dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
ipcdev.telem_res_inval = 1;
} else {
ipcdev.telem_punit_ssram_base = res->start +
TELEM_PUNIT_SSRAM_OFFSET;
ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
ipcdev.telem_pmc_ssram_base = res->start +
TELEM_PMC_SSRAM_OFFSET;
ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
}
return 0;
}
/**
* intel_pmc_s0ix_counter_read() - Read S0ix residency.
* @data: Out param that contains current S0ix residency count.
*
* Return: an error code or 0 on success.
*/
int intel_pmc_s0ix_counter_read(u64 *data)
{
u64 deep, shlw;
if (!ipcdev.has_gcr_regs)
return -EACCES;
deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
return 0;
}
EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
#ifdef CONFIG_ACPI
static const struct acpi_device_id ipc_acpi_ids[] = {
{ "INT34D2", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
#endif
static int ipc_plat_probe(struct platform_device *pdev)
{
int ret;
ipcdev.dev = &pdev->dev;
ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
init_completion(&ipcdev.cmd_complete);
spin_lock_init(&ipcdev.gcr_lock);
ipcdev.irq = platform_get_irq(pdev, 0);
if (ipcdev.irq < 0)
return -EINVAL;
ret = ipc_plat_get_res(pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to request resource\n");
return ret;
}
ret = ipc_create_pmc_devices();
if (ret) {
dev_err(&pdev->dev, "Failed to create pmc devices\n");
return ret;
}
if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
"intel_pmc_ipc", &ipcdev)) {
dev_err(&pdev->dev, "Failed to request irq\n");
ret = -EBUSY;
goto err_irq;
}
ipcdev.has_gcr_regs = true;
return 0;
err_irq:
platform_device_unregister(ipcdev.tco_dev);
platform_device_unregister(ipcdev.punit_dev);
platform_device_unregister(ipcdev.telemetry_dev);
return ret;
}
static int ipc_plat_remove(struct platform_device *pdev)
{
devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
platform_device_unregister(ipcdev.tco_dev);
platform_device_unregister(ipcdev.punit_dev);
platform_device_unregister(ipcdev.telemetry_dev);
ipcdev.dev = NULL;
return 0;
}
static struct platform_driver ipc_plat_driver = {
.remove = ipc_plat_remove,
.probe = ipc_plat_probe,
.driver = {
.name = "pmc-ipc-plat",
.acpi_match_table = ACPI_PTR(ipc_acpi_ids),
.dev_groups = intel_ipc_groups,
},
};
static int __init intel_pmc_ipc_init(void)
{
int ret;
ret = platform_driver_register(&ipc_plat_driver);
if (ret) {
pr_err("Failed to register PMC ipc platform driver\n");
return ret;
}
ret = pci_register_driver(&ipc_pci_driver);
if (ret) {
pr_err("Failed to register PMC ipc pci driver\n");
platform_driver_unregister(&ipc_plat_driver);
return ret;
}
return ret;
}
static void __exit intel_pmc_ipc_exit(void)
{
pci_unregister_driver(&ipc_pci_driver);
platform_driver_unregister(&ipc_plat_driver);
}
MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
MODULE_DESCRIPTION("Intel PMC IPC driver");
MODULE_LICENSE("GPL v2");
/* Some modules are dependent on this, so init earlier */
fs_initcall(intel_pmc_ipc_init);
module_exit(intel_pmc_ipc_exit);

Melihat File

@@ -18,11 +18,10 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/sfi.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
/* IPC defines the following message types */
@@ -55,14 +54,14 @@
#define IPC_IOC 0x100 /* IPC command register IOC bit */
struct intel_scu_ipc_dev {
struct device *dev;
struct device dev;
struct resource mem;
struct module *owner;
int irq;
void __iomem *ipc_base;
struct completion cmd_complete;
u8 irq_mode;
};
static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
#define IPC_STATUS 0x04
#define IPC_STATUS_IRQ BIT(2)
#define IPC_STATUS_ERR BIT(1)
@@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
/* Timeout in jiffies */
#define IPC_TIMEOUT (3 * HZ)
static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */
static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
static struct class intel_scu_ipc_class = {
.name = "intel_scu_ipc",
.owner = THIS_MODULE,
};
/**
* intel_scu_ipc_dev_get() - Get SCU IPC instance
*
* The recommended new API takes SCU IPC instance as parameter and this
* function can be called by driver to get the instance. This also makes
* sure the driver providing the IPC functionality cannot be unloaded
* while the caller has the instance.
*
* Call intel_scu_ipc_dev_put() to release the instance.
*
* Returns %NULL if SCU IPC is not currently available.
*/
struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
{
struct intel_scu_ipc_dev *scu = NULL;
mutex_lock(&ipclock);
if (ipcdev) {
get_device(&ipcdev->dev);
/*
* Prevent the IPC provider from being unloaded while it
* is being used.
*/
if (!try_module_get(ipcdev->owner))
put_device(&ipcdev->dev);
else
scu = ipcdev;
}
mutex_unlock(&ipclock);
return scu;
}
EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
/**
* intel_scu_ipc_dev_put() - Put SCU IPC instance
* @scu: SCU IPC instance
*
* This function releases the SCU IPC instance retrieved from
* intel_scu_ipc_dev_get() and allows the driver providing IPC to be
* unloaded.
*/
void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
{
if (scu) {
module_put(scu->owner);
put_device(&scu->dev);
}
}
EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
struct intel_scu_ipc_devres {
struct intel_scu_ipc_dev *scu;
};
static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
{
struct intel_scu_ipc_devres *dr = res;
struct intel_scu_ipc_dev *scu = dr->scu;
intel_scu_ipc_dev_put(scu);
}
/**
* devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
* @dev: Device requesting the SCU IPC device
*
* The recommended new API takes SCU IPC instance as parameter and this
* function can be called by driver to get the instance. This also makes
* sure the driver providing the IPC functionality cannot be unloaded
* while the caller has the instance.
*
* Returns %NULL if SCU IPC is not currently available.
*/
struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
{
struct intel_scu_ipc_devres *dr;
struct intel_scu_ipc_dev *scu;
dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
if (!dr)
return NULL;
scu = intel_scu_ipc_dev_get();
if (!scu) {
devres_free(dr);
return NULL;
}
dr->scu = scu;
devres_add(dev, dr);
return scu;
}
EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
/*
* Send ipc command
* Command Register (Write Only):
@@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
usleep_range(50, 100);
} while (time_before(jiffies, end));
dev_err(scu->dev, "IPC timed out");
return -ETIMEDOUT;
}
@@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
{
int status;
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
dev_err(scu->dev, "IPC timed out\n");
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
return -ETIMEDOUT;
}
status = ipc_read_status(scu);
if (status & IPC_STATUS_ERR)
@@ -166,13 +264,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
{
return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
}
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
u32 count, u32 op, u32 id)
{
struct intel_scu_ipc_dev *scu = &ipcdev;
int nc;
u32 offset = 0;
int err;
@@ -182,8 +280,9 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
memset(cbuf, 0, sizeof(cbuf));
mutex_lock(&ipclock);
if (scu->dev == NULL) {
if (!scu)
scu = ipcdev;
if (!scu) {
mutex_unlock(&ipclock);
return -ENODEV;
}
@@ -222,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
}
/**
* intel_scu_ipc_ioread8 - read a word via the SCU
* intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
* @scu: Optional SCU IPC instance
* @addr: Register on SCU
* @data: Return pointer for read byte
*
@@ -231,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
*
* This function may sleep.
*/
int intel_scu_ipc_ioread8(u16 addr, u8 *data)
int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
{
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
}
EXPORT_SYMBOL(intel_scu_ipc_ioread8);
EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
/**
* intel_scu_ipc_iowrite8 - write a byte via the SCU
* intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
* @scu: Optional SCU IPC instance
* @addr: Register on SCU
* @data: Byte to write
*
@@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
*
* This function may sleep.
*/
int intel_scu_ipc_iowrite8(u16 addr, u8 data)
int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
{
return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
}
EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
/**
* intel_scu_ipc_readvv - read a set of registers
* intel_scu_ipc_dev_readv() - Read a set of registers
* @scu: Optional SCU IPC instance
* @addr: Register list
* @data: Bytes to return
* @len: Length of array
@@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
*
* This function may sleep.
*/
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
size_t len)
{
return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
}
EXPORT_SYMBOL(intel_scu_ipc_readv);
EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
/**
* intel_scu_ipc_writev - write a set of registers
* intel_scu_ipc_dev_writev() - Write a set of registers
* @scu: Optional SCU IPC instance
* @addr: Register list
* @data: Bytes to write
* @len: Length of array
@@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
*
* This function may sleep.
*/
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
size_t len)
{
return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
}
EXPORT_SYMBOL(intel_scu_ipc_writev);
EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
/**
* intel_scu_ipc_update_register - r/m/w a register
* intel_scu_ipc_dev_update() - Update a register
* @scu: Optional SCU IPC instance
* @addr: Register address
* @bits: Bits to update
* @data: Bits to update
* @mask: Mask of bits to update
*
* Read-modify-write power control unit register. The first data argument
@@ -305,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
* This function may sleep. Locking between SCU accesses is handled
* for the caller.
*/
int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
u8 mask)
{
u8 data[2] = { bits, mask };
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
u8 tmp[2] = { data, mask };
return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
}
EXPORT_SYMBOL(intel_scu_ipc_update_register);
EXPORT_SYMBOL(intel_scu_ipc_dev_update);
/**
* intel_scu_ipc_simple_command - send a simple command
* intel_scu_ipc_dev_simple_command() - Send a simple command
* @scu: Optional SCU IPC instance
* @cmd: Command
* @sub: Sub type
*
@@ -324,62 +432,89 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
* This function may sleep. Locking for SCU accesses is handled for the
* caller.
*/
int intel_scu_ipc_simple_command(int cmd, int sub)
int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
int sub)
{
struct intel_scu_ipc_dev *scu = &ipcdev;
u32 cmdval;
int err;
mutex_lock(&ipclock);
if (scu->dev == NULL) {
if (!scu)
scu = ipcdev;
if (!scu) {
mutex_unlock(&ipclock);
return -ENODEV;
}
ipc_command(scu, sub << 12 | cmd);
scu = ipcdev;
cmdval = sub << 12 | cmd;
ipc_command(scu, cmdval);
err = intel_scu_ipc_check_status(scu);
mutex_unlock(&ipclock);
if (err)
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
return err;
}
EXPORT_SYMBOL(intel_scu_ipc_simple_command);
EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
/**
* intel_scu_ipc_command - command with data
* intel_scu_ipc_command_with_size() - Command with data
* @scu: Optional SCU IPC instance
* @cmd: Command
* @sub: Sub type
* @in: Input data
* @inlen: Input length in dwords
* @inlen: Input length in bytes
* @size: Input size written to the IPC command register in whatever
* units (dword, byte) the particular firmware requires. Normally
* should be the same as @inlen.
* @out: Output data
* @outlen: Output length in dwords
* @outlen: Output length in bytes
*
* Issue a command to the SCU which involves data transfers. Do the
* data copies under the lock but leave it for the caller to interpret.
*/
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
u32 *out, int outlen)
int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
int sub, const void *in, size_t inlen,
size_t size, void *out, size_t outlen)
{
struct intel_scu_ipc_dev *scu = &ipcdev;
size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
u32 cmdval, inbuf[4] = {};
int i, err;
if (inbuflen > 4 || outbuflen > 4)
return -EINVAL;
mutex_lock(&ipclock);
if (scu->dev == NULL) {
if (!scu)
scu = ipcdev;
if (!scu) {
mutex_unlock(&ipclock);
return -ENODEV;
}
for (i = 0; i < inlen; i++)
ipc_data_writel(scu, *in++, 4 * i);
memcpy(inbuf, in, inlen);
for (i = 0; i < inbuflen; i++)
ipc_data_writel(scu, inbuf[i], 4 * i);
ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
cmdval = (size << 16) | (sub << 12) | cmd;
ipc_command(scu, cmdval);
err = intel_scu_ipc_check_status(scu);
if (!err) {
for (i = 0; i < outlen; i++)
*out++ = ipc_data_readl(scu, 4 * i);
u32 outbuf[4] = {};
for (i = 0; i < outbuflen; i++)
outbuf[i] = ipc_data_readl(scu, 4 * i);
memcpy(out, outbuf, outlen);
}
mutex_unlock(&ipclock);
if (err)
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
return err;
}
EXPORT_SYMBOL(intel_scu_ipc_command);
EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
/*
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
@@ -399,61 +534,179 @@ static irqreturn_t ioc(int irq, void *dev_id)
return IRQ_HANDLED;
}
/**
* ipc_probe - probe an Intel SCU IPC
* @pdev: the PCI device matching
* @id: entry in the match table
*
* Enable and install an intel SCU IPC. This appears in the PCI space
* but uses some hard coded addresses as well.
*/
static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void intel_scu_ipc_release(struct device *dev)
{
int err;
struct intel_scu_ipc_dev *scu = &ipcdev;
struct intel_scu_ipc_dev *scu;
if (scu->dev) /* We support only one SCU */
return -EBUSY;
err = pcim_enable_device(pdev);
if (err)
return err;
err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
if (err)
return err;
init_completion(&scu->cmd_complete);
scu->ipc_base = pcim_iomap_table(pdev)[0];
err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
scu);
if (err)
return err;
/* Assign device at last */
scu->dev = &pdev->dev;
intel_scu_devices_create();
pci_set_drvdata(pdev, scu);
return 0;
scu = container_of(dev, struct intel_scu_ipc_dev, dev);
if (scu->irq > 0)
free_irq(scu->irq, scu);
iounmap(scu->ipc_base);
release_mem_region(scu->mem.start, resource_size(&scu->mem));
kfree(scu);
}
static const struct pci_device_id pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x080e) },
{ PCI_VDEVICE(INTEL, 0x08ea) },
{ PCI_VDEVICE(INTEL, 0x11a0) },
{}
};
/**
* __intel_scu_ipc_register() - Register SCU IPC device
* @parent: Parent device
* @scu_data: Data used to configure SCU IPC
* @owner: Module registering the SCU IPC device
*
* Call this function to register SCU IPC mechanism under @parent.
* Returns pointer to the new SCU IPC device or ERR_PTR() in case of
* failure. The caller may use the returned instance if it needs to do
* SCU IPC calls itself.
*/
struct intel_scu_ipc_dev *
__intel_scu_ipc_register(struct device *parent,
const struct intel_scu_ipc_data *scu_data,
struct module *owner)
{
int err;
struct intel_scu_ipc_dev *scu;
void __iomem *ipc_base;
static struct pci_driver ipc_driver = {
.driver = {
.suppress_bind_attrs = true,
},
.name = "intel_scu_ipc",
.id_table = pci_ids,
.probe = ipc_probe,
};
builtin_pci_driver(ipc_driver);
mutex_lock(&ipclock);
/* We support only one IPC */
if (ipcdev) {
err = -EBUSY;
goto err_unlock;
}
scu = kzalloc(sizeof(*scu), GFP_KERNEL);
if (!scu) {
err = -ENOMEM;
goto err_unlock;
}
scu->owner = owner;
scu->dev.parent = parent;
scu->dev.class = &intel_scu_ipc_class;
scu->dev.release = intel_scu_ipc_release;
dev_set_name(&scu->dev, "intel_scu_ipc");
if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem),
"intel_scu_ipc")) {
err = -EBUSY;
goto err_free;
}
ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem));
if (!ipc_base) {
err = -ENOMEM;
goto err_release;
}
scu->ipc_base = ipc_base;
scu->mem = scu_data->mem;
scu->irq = scu_data->irq;
init_completion(&scu->cmd_complete);
if (scu->irq > 0) {
err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu);
if (err)
goto err_unmap;
}
/*
* After this point intel_scu_ipc_release() takes care of
* releasing the SCU IPC resources once refcount drops to zero.
*/
err = device_register(&scu->dev);
if (err) {
put_device(&scu->dev);
goto err_unlock;
}
/* Assign device at last */
ipcdev = scu;
mutex_unlock(&ipclock);
return scu;
err_unmap:
iounmap(ipc_base);
err_release:
release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem));
err_free:
kfree(scu);
err_unlock:
mutex_unlock(&ipclock);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
/**
* intel_scu_ipc_unregister() - Unregister SCU IPC
* @scu: SCU IPC handle
*
* This unregisters the SCU IPC device and releases the acquired
* resources once the refcount goes to zero.
*/
void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu)
{
mutex_lock(&ipclock);
if (!WARN_ON(!ipcdev)) {
ipcdev = NULL;
device_unregister(&scu->dev);
}
mutex_unlock(&ipclock);
}
EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister);
static void devm_intel_scu_ipc_unregister(struct device *dev, void *res)
{
struct intel_scu_ipc_devres *dr = res;
struct intel_scu_ipc_dev *scu = dr->scu;
intel_scu_ipc_unregister(scu);
}
/**
* __devm_intel_scu_ipc_register() - Register managed SCU IPC device
* @parent: Parent device
* @scu_data: Data used to configure SCU IPC
* @owner: Module registering the SCU IPC device
*
* Call this function to register managed SCU IPC mechanism under
* @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in
* case of failure. The caller may use the returned instance if it needs
* to do SCU IPC calls itself.
*/
struct intel_scu_ipc_dev *
__devm_intel_scu_ipc_register(struct device *parent,
const struct intel_scu_ipc_data *scu_data,
struct module *owner)
{
struct intel_scu_ipc_devres *dr;
struct intel_scu_ipc_dev *scu;
dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL);
if (!dr)
return NULL;
scu = __intel_scu_ipc_register(parent, scu_data, owner);
if (IS_ERR(scu)) {
devres_free(dr);
return scu;
}
dr->scu = scu;
devres_add(parent, dr);
return scu;
}
EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register);
static int __init intel_scu_ipc_init(void)
{
return class_register(&intel_scu_ipc_class);
}
subsys_initcall(intel_scu_ipc_init);
static void __exit intel_scu_ipc_exit(void)
{
class_unregister(&intel_scu_ipc_class);
}
module_exit(intel_scu_ipc_exit);

Melihat File

@@ -22,6 +22,9 @@
static int major;
struct intel_scu_ipc_dev *scu;
static DEFINE_MUTEX(scu_lock);
/* IOCTL commands */
#define INTE_SCU_IPC_REGISTER_READ 0
#define INTE_SCU_IPC_REGISTER_WRITE 1
@@ -52,12 +55,12 @@ static int scu_reg_access(u32 cmd, struct scu_ipc_data *data)
switch (cmd) {
case INTE_SCU_IPC_REGISTER_READ:
return intel_scu_ipc_readv(data->addr, data->data, count);
return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count);
case INTE_SCU_IPC_REGISTER_WRITE:
return intel_scu_ipc_writev(data->addr, data->data, count);
return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count);
case INTE_SCU_IPC_REGISTER_UPDATE:
return intel_scu_ipc_update_register(data->addr[0],
data->data[0], data->mask);
return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],
data->mask);
default:
return -ENOTTY;
}
@@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
return 0;
}
static int scu_ipc_open(struct inode *inode, struct file *file)
{
int ret = 0;
/* Only single open at the time */
mutex_lock(&scu_lock);
if (scu) {
ret = -EBUSY;
goto unlock;
}
scu = intel_scu_ipc_dev_get();
if (!scu)
ret = -ENODEV;
unlock:
mutex_unlock(&scu_lock);
return ret;
}
static int scu_ipc_release(struct inode *inode, struct file *file)
{
mutex_lock(&scu_lock);
intel_scu_ipc_dev_put(scu);
scu = NULL;
mutex_unlock(&scu_lock);
return 0;
}
static const struct file_operations scu_ipc_fops = {
.unlocked_ioctl = scu_ipc_ioctl,
.open = scu_ipc_open,
.release = scu_ipc_release,
};
static int __init ipc_module_init(void)

Melihat File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCI driver for the Intel SCU.
*
* Copyright (C) 2008-2010, 2015, 2020 Intel Corporation
* Authors: Sreedhara DS (sreedhara.ds@intel.com)
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
static int intel_scu_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
void (*setup_fn)(void) = (void (*)(void))id->driver_data;
struct intel_scu_ipc_data scu_data = {};
struct intel_scu_ipc_dev *scu;
int ret;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
scu_data.mem = pdev->resource[0];
scu_data.irq = pdev->irq;
scu = intel_scu_ipc_register(&pdev->dev, &scu_data);
if (IS_ERR(scu))
return PTR_ERR(scu);
if (setup_fn)
setup_fn();
return 0;
}
static void intel_mid_scu_setup(void)
{
intel_scu_devices_create();
}
static const struct pci_device_id pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x080e),
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
{ PCI_VDEVICE(INTEL, 0x08ea),
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
{ PCI_VDEVICE(INTEL, 0x0a94) },
{ PCI_VDEVICE(INTEL, 0x11a0),
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
{ PCI_VDEVICE(INTEL, 0x1a94) },
{ PCI_VDEVICE(INTEL, 0x5a94) },
{}
};
static struct pci_driver intel_scu_pci_driver = {
.driver = {
.suppress_bind_attrs = true,
},
.name = "intel_scu",
.id_table = pci_ids,
.probe = intel_scu_pci_probe,
};
builtin_pci_driver(intel_scu_pci_driver);

Melihat File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Platform driver for the Intel SCU.
*
* Copyright (C) 2019, Intel Corporation
* Authors: Divya Sasidharan <divya.s.sasidharan@intel.com>
* Mika Westerberg <mika.westerberg@linux.intel.com>
* Rajmohan Mani <rajmohan.mani@intel.com>
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/intel_scu_ipc.h>
static int intel_scu_platform_probe(struct platform_device *pdev)
{
struct intel_scu_ipc_data scu_data = {};
struct intel_scu_ipc_dev *scu;
const struct resource *res;
scu_data.irq = platform_get_irq_optional(pdev, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOMEM;
scu_data.mem = *res;
scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data);
if (IS_ERR(scu))
return PTR_ERR(scu);
platform_set_drvdata(pdev, scu);
return 0;
}
static const struct acpi_device_id intel_scu_acpi_ids[] = {
{ "INTC1026" },
{}
};
MODULE_DEVICE_TABLE(acpi, intel_scu_acpi_ids);
static struct platform_driver intel_scu_platform_driver = {
.probe = intel_scu_platform_probe,
.driver = {
.name = "intel_scu",
.acpi_match_table = intel_scu_acpi_ids,
},
};
module_platform_driver(intel_scu_platform_driver);
MODULE_AUTHOR("Divya Sasidharan <divya.s.sasidharan@intel.com>");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com");
MODULE_AUTHOR("Rajmohan Mani <rajmohan.mani@intel.com>");
MODULE_DESCRIPTION("Intel SCU platform driver");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -21,13 +21,12 @@
#define PUNIT_MAILBOX_BUSY_BIT 31
/*
* Commands has variable amount of processing time. Most of the commands will
* be done in 0-3 tries, but some takes up to 50.
* The real processing time was observed as 25us for the most of the commands
* at 2GHz. It is possible to optimize this count taking samples on customer
* systems.
* The average time to complete some commands is about 40us. The current
* count is enough to satisfy 40us. But when the firmware is very busy, this
* causes timeout occasionally. So increase to deal with some worst case
* scenarios. Most of the command still complete in few us.
*/
#define OS_MAILBOX_RETRY_COUNT 50
#define OS_MAILBOX_RETRY_COUNT 100
struct isst_if_device {
struct mutex mutex;

Melihat File

@@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void)
EXPORT_SYMBOL_GPL(telemetry_clear_pltdata);
/**
* telemetry_pltconfig_valid() - Checkif platform config is valid
* telemetry_get_pltdata() - Return telemetry platform config
*
* Usage by other than telemetry module is invalid
*
* Return: 0 success, < 0 for failure
* May be used by other telemetry modules to get platform specific
* configuration.
*/
int telemetry_pltconfig_valid(void)
struct telemetry_plt_config *telemetry_get_pltdata(void)
{
if (telm_core_conf.plt_config)
return 0;
else
return -EINVAL;
return telm_core_conf.plt_config;
}
EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid);
EXPORT_SYMBOL_GPL(telemetry_get_pltdata);
static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
const char **name, int len)

Melihat File

@@ -15,6 +15,7 @@
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/mfd/intel_pmc_bxt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
@@ -22,7 +23,6 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/intel_pmc_ipc.h>
#include <asm/intel_telemetry.h>
#define DRIVER_NAME "telemetry_soc_debugfs"
@@ -647,10 +647,11 @@ DEFINE_SHOW_ATTRIBUTE(telem_soc_states);
static int telem_s0ix_res_get(void *data, u64 *val)
{
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
u64 s0ix_total_res;
int ret;
ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
ret = intel_pmc_s0ix_counter_read(plt_config->pmc, &s0ix_total_res);
if (ret) {
pr_err("Failed to read S0ix residency");
return ret;
@@ -837,12 +838,15 @@ static int pm_suspend_exit_cb(void)
*/
if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
suspend_deep_ctr_exit == suspend_deep_ctr_temp) {
ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG,
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
struct intel_pmc_dev *pmc = plt_config->pmc;
ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_SHLW_S0IX_REG,
&suspend_shlw_res_exit);
if (ret < 0)
goto out;
ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG,
ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_DEEP_S0IX_REG,
&suspend_deep_res_exit);
if (ret < 0)
goto out;
@@ -910,8 +914,7 @@ static int __init telemetry_debugfs_init(void)
debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data;
err = telemetry_pltconfig_valid();
if (err < 0) {
if (!telemetry_get_pltdata()) {
pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n");
return -ENODEV;
}

Melihat File

@@ -15,7 +15,6 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/intel_pmc_ipc.h>
#include <asm/intel_punit_ipc.h>
#include <asm/intel_telemetry.h>
@@ -35,6 +34,7 @@
#define TELEM_SSRAM_STARTTIME_OFFSET 8
#define TELEM_SSRAM_EVTLOG_OFFSET 16
#define IOSS_TELEM 0xeb
#define IOSS_TELEM_EVENT_READ 0x0
#define IOSS_TELEM_EVENT_WRITE 0x1
#define IOSS_TELEM_INFO_READ 0x2
@@ -42,9 +42,6 @@
#define IOSS_TELEM_TRACE_CTL_WRITE 0x6
#define IOSS_TELEM_EVENT_CTL_READ 0x7
#define IOSS_TELEM_EVENT_CTL_WRITE 0x8
#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE 0x4
#define IOSS_TELEM_READ_WORD 0x1
#define IOSS_TELEM_WRITE_FOURBYTES 0x4
#define IOSS_TELEM_EVT_WRITE_SIZE 0x3
#define TELEM_INFO_SRAMEVTS_MASK 0xFF00
@@ -250,17 +247,14 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
{
u32 write_buf;
int ret;
write_buf = evt_id | TELEM_EVENT_ENABLE;
write_buf <<= BITS_PER_BYTE;
write_buf |= index;
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
return ret;
return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_EVENT_WRITE, &write_buf,
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
}
static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
@@ -278,6 +272,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
enum telemetry_action action)
{
struct intel_scu_ipc_dev *scu = telm_conf->scu;
u8 num_ioss_evts, ioss_period;
int ret, index, idx;
u32 *ioss_evtmap;
@@ -288,9 +283,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
ioss_evtmap = evtconfig.evtmap;
/* Get telemetry EVENT CTL */
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
&telem_ctrl, IOSS_TELEM_READ_WORD);
&telem_ctrl, sizeof(telem_ctrl));
if (ret) {
pr_err("IOSS TELEM_CTRL Read Failed\n");
return ret;
@@ -299,11 +294,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
/* Disable Telemetry */
TELEM_DISABLE(telem_ctrl);
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl,
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
NULL, 0);
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl,
sizeof(telem_ctrl), NULL, 0);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
return ret;
@@ -315,10 +308,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
/* Clear All Events */
TELEM_CLEAR_EVENTS(telem_ctrl);
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl,
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
&telem_ctrl, sizeof(telem_ctrl),
NULL, 0);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
@@ -344,10 +336,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
/* Clear All Events */
TELEM_CLEAR_EVENTS(telem_ctrl);
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl,
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
&telem_ctrl, sizeof(telem_ctrl),
NULL, 0);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
@@ -396,10 +387,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
TELEM_ENABLE_PERIODIC(telem_ctrl);
telem_ctrl |= ioss_period;
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl,
IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
&telem_ctrl, sizeof(telem_ctrl), NULL, 0);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
return ret;
@@ -586,8 +576,9 @@ static int telemetry_setup(struct platform_device *pdev)
u32 read_buf, events, event_regs;
int ret;
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_INFO_READ, NULL, 0,
&read_buf, sizeof(read_buf));
if (ret) {
dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
return ret;
@@ -681,6 +672,8 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
mutex_lock(&(telm_conf->telem_lock));
if (ioss_period) {
struct intel_scu_ipc_dev *scu = telm_conf->scu;
if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
pr_err("IOSS Sampling Period Out of Range\n");
ret = -EINVAL;
@@ -688,9 +681,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
}
/* Get telemetry EVENT CTL */
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
&telem_ctrl, IOSS_TELEM_READ_WORD);
&telem_ctrl, sizeof(telem_ctrl));
if (ret) {
pr_err("IOSS TELEM_CTRL Read Failed\n");
goto out;
@@ -699,11 +692,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
/* Disable Telemetry */
TELEM_DISABLE(telem_ctrl);
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl,
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
NULL, 0);
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_WRITE,
&telem_ctrl, sizeof(telem_ctrl),
NULL, 0);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
goto out;
@@ -715,11 +707,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
TELEM_ENABLE_PERIODIC(telem_ctrl);
telem_ctrl |= ioss_period;
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl,
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
NULL, 0);
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
IOSS_TELEM_EVENT_CTL_WRITE,
&telem_ctrl, sizeof(telem_ctrl),
NULL, 0);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
goto out;
@@ -1014,9 +1005,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
break;
case TELEM_IOSS:
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
IOSS_TELEM_READ_WORD);
ret = intel_scu_ipc_dev_command(telm_conf->scu,
IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ,
NULL, 0, &temp, sizeof(temp));
if (ret) {
pr_err("IOSS TRACE_CTL Read Failed\n");
goto out;
@@ -1068,9 +1059,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
break;
case TELEM_IOSS:
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
IOSS_TELEM_READ_WORD);
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_TRACE_CTL_READ,
NULL, 0, &temp, sizeof(temp));
if (ret) {
pr_err("IOSS TRACE_CTL Read Failed\n");
goto out;
@@ -1079,9 +1070,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
TELEM_CLEAR_VERBOSITY_BITS(temp);
TELEM_SET_VERBOSITY_BITS(temp, verbosity);
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_TRACE_CTL_WRITE,
&temp, sizeof(temp), NULL, 0);
if (ret) {
pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
goto out;
@@ -1124,6 +1115,8 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
telm_conf = (struct telemetry_plt_config *)id->driver_data;
telm_conf->pmc = dev_get_drvdata(pdev->dev.parent);
mem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mem))
return PTR_ERR(mem);
@@ -1136,6 +1129,12 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
telm_conf->ioss_config.regmap = mem;
telm_conf->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
if (!telm_conf->scu) {
ret = -EPROBE_DEFER;
goto out;
}
mutex_init(&telm_conf->telem_lock);
mutex_init(&telm_conf->telem_trace_lock);

Melihat File

@@ -67,9 +67,7 @@ static u32 inited;
#define INIT_INPUT_WMI_0 0x01
#define INIT_INPUT_WMI_2 0x02
#define INIT_INPUT_ACPI 0x04
#define INIT_TPAD_LED 0x08
#define INIT_KBD_LED 0x10
#define INIT_SPARSE_KEYMAP 0x80
#define INIT_SPARSE_KEYMAP 0x80
static const struct key_entry wmi_keymap[] = {
{KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
@@ -626,11 +624,9 @@ static int acpi_add(struct acpi_device *device)
if (ret)
goto out_platform_device;
if (!led_classdev_register(&pf_device->dev, &kbd_backlight))
inited |= INIT_KBD_LED;
if (!led_classdev_register(&pf_device->dev, &tpad_led))
inited |= INIT_TPAD_LED;
/* LEDs are optional */
led_classdev_register(&pf_device->dev, &kbd_backlight);
led_classdev_register(&pf_device->dev, &tpad_led);
wmi_input_setup();
@@ -646,11 +642,9 @@ out_platform_registered:
static int acpi_remove(struct acpi_device *device)
{
sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
if (inited & INIT_KBD_LED)
led_classdev_unregister(&kbd_backlight);
if (inited & INIT_TPAD_LED)
led_classdev_unregister(&tpad_led);
led_classdev_unregister(&tpad_led);
led_classdev_unregister(&kbd_backlight);
wmi_input_destroy();
platform_device_unregister(pf_device);

Melihat File

@@ -1138,8 +1138,7 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
static void samsung_leds_exit(struct samsung_laptop *samsung)
{
if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
led_classdev_unregister(&samsung->kbd_led);
led_classdev_unregister(&samsung->kbd_led);
if (samsung->led_workqueue)
destroy_workqueue(samsung->led_workqueue);
}

Melihat File

@@ -757,33 +757,6 @@ static union acpi_object *__call_snc_method(acpi_handle handle, char *method,
return result;
}
static int sony_nc_int_call(acpi_handle handle, char *name, int *value,
int *result)
{
union acpi_object *object = NULL;
if (value) {
u64 v = *value;
object = __call_snc_method(handle, name, &v);
} else
object = __call_snc_method(handle, name, NULL);
if (!object)
return -EINVAL;
if (object->type != ACPI_TYPE_INTEGER) {
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
ACPI_TYPE_INTEGER, object->type);
kfree(object);
return -EINVAL;
}
if (result)
*result = object->integer.value;
kfree(object);
return 0;
}
#define MIN(a, b) (a > b ? b : a)
static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
void *buffer, size_t buflen)
@@ -795,17 +768,20 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
if (!object)
return -EINVAL;
if (object->type == ACPI_TYPE_BUFFER) {
if (!buffer) {
/* do nothing */
} else if (object->type == ACPI_TYPE_BUFFER) {
len = MIN(buflen, object->buffer.length);
memset(buffer, 0, buflen);
memcpy(buffer, object->buffer.pointer, len);
} else if (object->type == ACPI_TYPE_INTEGER) {
len = MIN(buflen, sizeof(object->integer.value));
memset(buffer, 0, buflen);
memcpy(buffer, &object->integer.value, len);
} else {
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
ACPI_TYPE_BUFFER, object->type);
pr_warn("Unexpected acpi_object: 0x%x\n", object->type);
ret = -EINVAL;
}
@@ -813,6 +789,23 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
return ret;
}
static int sony_nc_int_call(acpi_handle handle, char *name, int *value, int
*result)
{
int ret;
if (value) {
u64 v = *value;
ret = sony_nc_buffer_call(handle, name, &v, result,
sizeof(*result));
} else {
ret = sony_nc_buffer_call(handle, name, NULL, result,
sizeof(*result));
}
return ret;
}
struct sony_nc_handles {
u16 cap[0x10];
struct device_attribute devattr;
@@ -2295,7 +2288,12 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd)
#ifdef CONFIG_PM_SLEEP
static void sony_nc_thermal_resume(void)
{
unsigned int status = sony_nc_thermal_mode_get();
int status;
if (!th_handle)
return;
status = sony_nc_thermal_mode_get();
if (status != th_handle->mode)
sony_nc_thermal_mode_set(th_handle->mode);

Melihat File

@@ -318,6 +318,7 @@ static struct {
u32 uwb:1;
u32 fan_ctrl_status_undef:1;
u32 second_fan:1;
u32 second_fan_ctl:1;
u32 beep_needs_two_args:1;
u32 mixer_no_level_control:1;
u32 battery_force_primary:1;
@@ -884,20 +885,11 @@ static ssize_t dispatch_proc_write(struct file *file,
if (!ibm || !ibm->write)
return -EINVAL;
if (count > PAGE_SIZE - 2)
return -EINVAL;
kernbuf = kmalloc(count + 2, GFP_KERNEL);
if (!kernbuf)
return -ENOMEM;
kernbuf = strndup_user(userbuf, PAGE_SIZE);
if (IS_ERR(kernbuf))
return PTR_ERR(kernbuf);
if (copy_from_user(kernbuf, userbuf, count)) {
kfree(kernbuf);
return -EFAULT;
}
kernbuf[count] = 0;
strcat(kernbuf, ",");
ret = ibm->write(kernbuf);
if (ret == 0)
ret = count;
@@ -915,23 +907,6 @@ static const struct proc_ops dispatch_proc_ops = {
.proc_write = dispatch_proc_write,
};
static char *next_cmd(char **cmds)
{
char *start = *cmds;
char *end;
while ((end = strchr(start, ',')) && end == start)
start = end + 1;
if (!end)
return NULL;
*end = 0;
*cmds = end + 1;
return start;
}
/****************************************************************************
****************************************************************************
*
@@ -1422,7 +1397,7 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
if (id >= TPACPI_RFK_SW_MAX)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "enable") == 0)
status = TPACPI_RFK_RADIO_ON;
else if (strlencmp(cmd, "disable") == 0)
@@ -4305,7 +4280,7 @@ static int hotkey_write(char *buf)
mask = hotkey_user_mask;
res = 0;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "enable") == 0) {
hotkey_enabledisable_warn(1);
} else if (strlencmp(cmd, "disable") == 0) {
@@ -5232,7 +5207,7 @@ static int video_write(char *buf)
enable = 0;
disable = 0;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "lcd_enable") == 0) {
enable |= TP_ACPI_VIDEO_S_LCD;
} else if (strlencmp(cmd, "lcd_disable") == 0) {
@@ -5433,8 +5408,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
static void kbdlight_exit(void)
{
if (tp_features.kbdlight)
led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
}
static int kbdlight_set_level_and_update(int level)
@@ -5472,23 +5446,18 @@ static int kbdlight_read(struct seq_file *m)
static int kbdlight_write(char *buf)
{
char *cmd;
int level = -1;
int res, level = -EINVAL;
if (!tp_features.kbdlight)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "0") == 0)
level = 0;
else if (strlencmp(cmd, "1") == 0)
level = 1;
else if (strlencmp(cmd, "2") == 0)
level = 2;
else
return -EINVAL;
while ((cmd = strsep(&buf, ","))) {
res = kstrtoint(cmd, 10, &level);
if (res < 0)
return res;
}
if (level == -1)
if (level >= 3 || level < 0)
return -EINVAL;
return kbdlight_set_level_and_update(level);
@@ -5657,7 +5626,7 @@ static int light_write(char *buf)
if (!tp_features.light)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "on") == 0) {
newstatus = 1;
} else if (strlencmp(cmd, "off") == 0) {
@@ -5742,7 +5711,7 @@ static int cmos_write(char *buf)
char *cmd;
int cmos_cmd, res;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
cmos_cmd >= 0 && cmos_cmd <= 21) {
/* cmos_cmd set */
@@ -5948,20 +5917,14 @@ static void led_exit(void)
{
unsigned int i;
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
if (tpacpi_leds[i].led_classdev.name)
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
}
for (i = 0; i < TPACPI_LED_NUMLEDS; i++)
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
kfree(tpacpi_leds);
}
static int __init tpacpi_init_led(unsigned int led)
{
int rc;
tpacpi_leds[led].led = led;
/* LEDs with no name don't get registered */
if (!tpacpi_led_names[led])
return 0;
@@ -5969,17 +5932,12 @@ static int __init tpacpi_init_led(unsigned int led)
tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
if (led_supported == TPACPI_LED_570)
tpacpi_leds[led].led_classdev.brightness_get =
&led_sysfs_get;
tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
tpacpi_leds[led].led = led;
rc = led_classdev_register(&tpacpi_pdev->dev,
&tpacpi_leds[led].led_classdev);
if (rc < 0)
tpacpi_leds[led].led_classdev.name = NULL;
return rc;
return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
}
static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
@@ -6089,8 +6047,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
tpacpi_leds[i].led = -1;
if (!tpacpi_is_led_restricted(i) &&
test_bit(i, &useful_leds)) {
if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) {
rc = tpacpi_init_led(i);
if (rc < 0) {
led_exit();
@@ -6143,12 +6100,14 @@ static int led_write(char *buf)
if (!led_supported)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%d", &led) != 1)
return -EINVAL;
if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
tpacpi_leds[led].led < 0)
if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1))
return -ENODEV;
if (tpacpi_leds[led].led < 0)
return -ENODEV;
if (strstr(cmd, "off")) {
@@ -6228,7 +6187,7 @@ static int beep_write(char *buf)
if (!beep_handle)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
beep_cmd >= 0 && beep_cmd <= 17) {
/* beep_cmd set */
@@ -7116,7 +7075,7 @@ static int brightness_write(char *buf)
if (level < 0)
return level;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "up") == 0) {
if (level < bright_maxlvl)
level++;
@@ -7868,7 +7827,7 @@ static int volume_write(char *buf)
new_level = s & TP_EC_AUDIO_LVL_MSK;
new_mute = s & TP_EC_AUDIO_MUTESW_MSK;
while ((cmd = next_cmd(&buf))) {
while ((cmd = strsep(&buf, ","))) {
if (!tp_features.mixer_no_level_control) {
if (strlencmp(cmd, "up") == 0) {
if (new_mute)
@@ -8324,11 +8283,19 @@ static int fan_set_level(int level)
switch (fan_control_access_mode) {
case TPACPI_FAN_WR_ACPI_SFAN:
if (level >= 0 && level <= 7) {
if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
return -EIO;
} else
if ((level < 0) || (level > 7))
return -EINVAL;
if (tp_features.second_fan_ctl) {
if (!fan_select_fan2() ||
!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) {
pr_warn("Couldn't set 2nd fan level, disabling support\n");
tp_features.second_fan_ctl = 0;
}
fan_select_fan1();
}
if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
return -EIO;
break;
case TPACPI_FAN_WR_ACPI_FANS:
@@ -8345,6 +8312,15 @@ static int fan_set_level(int level)
else if (level & TP_EC_FAN_AUTO)
level |= 4; /* safety min speed 4 */
if (tp_features.second_fan_ctl) {
if (!fan_select_fan2() ||
!acpi_ec_write(fan_status_offset, level)) {
pr_warn("Couldn't set 2nd fan level, disabling support\n");
tp_features.second_fan_ctl = 0;
}
fan_select_fan1();
}
if (!acpi_ec_write(fan_status_offset, level))
return -EIO;
else
@@ -8763,6 +8739,7 @@ static const struct attribute_group fan_attr_group = {
#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */
#define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
@@ -8771,6 +8748,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_QEC_IBM('7', '0', TPACPI_FAN_Q1),
TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN),
TPACPI_Q_LNV('N', '1', TPACPI_FAN_2FAN),
TPACPI_Q_LNV3('N', '1', 'D', TPACPI_FAN_2CTL), /* P70 */
TPACPI_Q_LNV3('N', '1', 'E', TPACPI_FAN_2CTL), /* P50 */
TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL), /* P71 */
TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL), /* P51 */
TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL), /* P52 / P72 */
TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (1st gen) */
TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (2nd gen) */
};
static int __init fan_init(struct ibm_init_struct *iibm)
@@ -8788,6 +8772,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
fan_watchdog_maxinterval = 0;
tp_features.fan_ctrl_status_undef = 0;
tp_features.second_fan = 0;
tp_features.second_fan_ctl = 0;
fan_control_desired_level = 7;
if (tpacpi_is_ibm()) {
@@ -8812,8 +8797,12 @@ static int __init fan_init(struct ibm_init_struct *iibm)
fan_quirk1_setup();
if (quirks & TPACPI_FAN_2FAN) {
tp_features.second_fan = 1;
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
"secondary fan support enabled\n");
pr_info("secondary fan support enabled\n");
}
if (quirks & TPACPI_FAN_2CTL) {
tp_features.second_fan = 1;
tp_features.second_fan_ctl = 1;
pr_info("secondary fan control enabled\n");
}
} else {
pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
@@ -9148,7 +9137,7 @@ static int fan_write(char *buf)
char *cmd;
int rc = 0;
while (!rc && (cmd = next_cmd(&buf))) {
while (!rc && (cmd = strsep(&buf, ","))) {
if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
fan_write_cmd_level(cmd, &rc)) &&
!((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
@@ -9271,10 +9260,8 @@ static int mute_led_init(struct ibm_init_struct *iibm)
mute_led_cdev[i].brightness = ledtrig_audio_get(i);
err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
if (err < 0) {
while (i--) {
if (led_tables[i].state >= 0)
led_classdev_unregister(&mute_led_cdev[i]);
}
while (i--)
led_classdev_unregister(&mute_led_cdev[i]);
return err;
}
}
@@ -9286,10 +9273,8 @@ static void mute_led_exit(void)
int i;
for (i = 0; i < TPACPI_LED_MAX; i++) {
if (led_tables[i].state >= 0) {
led_classdev_unregister(&mute_led_cdev[i]);
tpacpi_led_set(i, false);
}
led_classdev_unregister(&mute_led_cdev[i]);
tpacpi_led_set(i, false);
}
}
@@ -9786,19 +9771,18 @@ static int lcdshadow_read(struct seq_file *m)
static int lcdshadow_write(char *buf)
{
char *cmd;
int state = -1;
int res, state = -EINVAL;
if (lcdshadow_state < 0)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "0") == 0)
state = 0;
else if (strlencmp(cmd, "1") == 0)
state = 1;
while ((cmd = strsep(&buf, ","))) {
res = kstrtoint(cmd, 10, &state);
if (res < 0)
return res;
}
if (state == -1)
if (state >= 2 || state < 0)
return -EINVAL;
return lcdshadow_set(state);
@@ -10314,10 +10298,9 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
continue;
if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
if (strlen(val) > sizeof(ibms_init[i].param) - 2)
if (strlen(val) > sizeof(ibms_init[i].param) - 1)
return -ENOSPC;
strcpy(ibms_init[i].param, val);
strcat(ibms_init[i].param, ",");
return 0;
}
}

Melihat File

@@ -205,9 +205,6 @@ struct toshiba_acpi_dev {
unsigned int special_functions;
bool kbd_event_generated;
bool kbd_led_registered;
bool illumination_led_registered;
bool eco_led_registered;
bool killswitch;
};
@@ -458,7 +455,6 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
acpi_status status;
dev->illumination_supported = 0;
dev->illumination_led_registered = false;
if (!sci_open(dev))
return;
@@ -528,7 +524,6 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
acpi_status status;
dev->kbd_illum_supported = 0;
dev->kbd_led_registered = false;
dev->kbd_event_generated = false;
if (!sci_open(dev))
@@ -673,7 +668,6 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
acpi_status status;
dev->eco_supported = 0;
dev->eco_led_registered = false;
status = tci_raw(dev, in, out);
if (ACPI_FAILURE(status)) {
@@ -2993,14 +2987,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
backlight_device_unregister(dev->backlight_dev);
if (dev->illumination_led_registered)
led_classdev_unregister(&dev->led_dev);
if (dev->kbd_led_registered)
led_classdev_unregister(&dev->kbd_led);
if (dev->eco_led_registered)
led_classdev_unregister(&dev->eco_led);
led_classdev_unregister(&dev->led_dev);
led_classdev_unregister(&dev->kbd_led);
led_classdev_unregister(&dev->eco_led);
if (dev->wwan_rfk) {
rfkill_unregister(dev->wwan_rfk);
@@ -3092,8 +3081,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->led_dev.max_brightness = 1;
dev->led_dev.brightness_set = toshiba_illumination_set;
dev->led_dev.brightness_get = toshiba_illumination_get;
if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
dev->illumination_led_registered = true;
led_classdev_register(&acpi_dev->dev, &dev->led_dev);
}
toshiba_eco_mode_available(dev);
@@ -3102,8 +3090,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->eco_led.max_brightness = 1;
dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
dev->eco_led_registered = true;
led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led);
}
toshiba_kbd_illum_available(dev);
@@ -3119,8 +3106,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->kbd_led.max_brightness = 1;
dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
dev->kbd_led_registered = true;
led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led);
}
ret = toshiba_touchpad_get(dev, &dummy);

Melihat File

@@ -373,6 +373,23 @@ static const struct ts_dmi_data jumper_ezpad_mini3_data = {
.properties = jumper_ezpad_mini3_props,
};
static const struct property_entry mpman_mpwin895cl_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 3),
PROPERTY_ENTRY_U32("touchscreen-min-y", 9),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-mpman-mpwin895cl.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data mpman_mpwin895cl_data = {
.acpi_name = "MSSL1680:00",
.properties = mpman_mpwin895cl_props,
};
static const struct property_entry myria_my8307_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1720),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
@@ -448,6 +465,24 @@ static const struct ts_dmi_data onda_v820w_32g_data = {
.properties = onda_v820w_32g_props,
};
static const struct property_entry onda_v891_v5_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl3676-onda-v891-v5.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data onda_v891_v5_data = {
.acpi_name = "MSSL1680:00",
.properties = onda_v891_v5_props,
};
static const struct property_entry onda_v891w_v1_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 46),
PROPERTY_ENTRY_U32("touchscreen-min-y", 8),
@@ -588,6 +623,22 @@ static const struct ts_dmi_data schneider_sct101ctm_data = {
.properties = schneider_sct101ctm_props,
};
static const struct property_entry techbite_arc_11_6_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 5),
PROPERTY_ENTRY_U32("touchscreen-min-y", 7),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1981),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1270),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-techbite-arc-11-6.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
static const struct ts_dmi_data techbite_arc_11_6_data = {
.acpi_name = "MSSL1680:00",
.properties = techbite_arc_11_6_props,
};
static const struct property_entry teclast_x3_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
@@ -662,11 +713,14 @@ static const struct ts_dmi_data trekstor_primetab_t13b_data = {
};
static const struct property_entry trekstor_surftab_twin_10_1_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1900),
PROPERTY_ENTRY_U32("touchscreen-min-x", 20),
PROPERTY_ENTRY_U32("touchscreen-min-y", 0),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1890),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -691,6 +745,20 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = {
.properties = trekstor_surftab_wintron70_props,
};
static const struct property_entry vinga_twizzle_j116_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1920),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-vinga-twizzle_j116.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data vinga_twizzle_j116_data = {
.acpi_name = "MSSL1680:00",
.properties = vinga_twizzle_j116_props,
};
/* NOTE: Please keep this table sorted alphabetically */
const struct dmi_system_id touchscreen_dmi_table[] = {
{
@@ -908,6 +976,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
},
},
{
/* MP Man MPWIN895CL */
.driver_data = (void *)&mpman_mpwin895cl_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MPMAN"),
DMI_MATCH(DMI_PRODUCT_NAME, "MPWIN8900CL"),
},
},
{
/* Myria MY8307 */
.driver_data = (void *)&myria_my8307_data,
@@ -940,6 +1016,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "V820w DualOS")
},
},
{
/* ONDA V891 v5 */
.driver_data = (void *)&onda_v891_v5_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ONDA"),
DMI_MATCH(DMI_PRODUCT_NAME, "ONDA Tablet"),
DMI_MATCH(DMI_BIOS_VERSION, "ONDA.D869CJABNRBA06"),
},
},
{
/* ONDA V891w revision P891WBEBV1B00 aka v1 */
.driver_data = (void *)&onda_v891w_v1_data,
@@ -1029,6 +1114,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "SCT101CTM"),
},
},
{
/* Techbite Arc 11.6 */
.driver_data = (void *)&techbite_arc_11_6_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "mPTech"),
DMI_MATCH(DMI_PRODUCT_NAME, "techBite Arc 11.6"),
DMI_MATCH(DMI_BOARD_NAME, "G8316_272B"),
},
},
{
/* Teclast X3 Plus */
.driver_data = (void *)&teclast_x3_plus_data,
@@ -1106,6 +1200,21 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA05"),
},
},
{
/* Trekstor Yourbook C11B (same touchscreen as the Primebook C11) */
.driver_data = (void *)&trekstor_primebook_c11_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
DMI_MATCH(DMI_PRODUCT_NAME, "YOURBOOK C11B"),
},
},
{
/* Vinga Twizzle J116 */
.driver_data = (void *)&vinga_twizzle_j116_data,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"),
},
},
{
/* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
.driver_data = (void *)&chuwi_vi8_data,
@@ -1114,7 +1223,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
},
},
{ },
{ }
};
static const struct ts_dmi_data *ts_data;

Melihat File

@@ -111,11 +111,11 @@ static struct platform_driver acpi_wmi_driver = {
static bool find_guid(const char *guid_string, struct wmi_block **out)
{
uuid_le guid_input;
guid_t guid_input;
struct wmi_block *wblock;
struct guid_block *block;
if (uuid_le_to_bin(guid_string, &guid_input))
if (guid_parse(guid_string, &guid_input))
return false;
list_for_each_entry(wblock, &wmi_block_list, list) {
@@ -134,7 +134,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
struct wmi_driver *wdriver)
{
const struct wmi_device_id *id;
uuid_le guid_input;
guid_t guid_input;
if (wblock == NULL || wdriver == NULL)
return NULL;
@@ -143,7 +143,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
id = wdriver->id_table;
while (*id->guid_string) {
if (uuid_le_to_bin(id->guid_string, &guid_input))
if (guid_parse(id->guid_string, &guid_input))
continue;
if (!memcmp(wblock->gblock.guid, &guid_input, 16))
return id->context;
@@ -202,7 +202,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
/**
* set_required_buffer_size - Sets the buffer size needed for performing IOCTL
* @wdev: A wmi bus device from a driver
* @instance: Instance index
* @length: Required buffer size
*
* Allocates memory needed for buffer, stores the buffer size in that memory
*/
@@ -222,8 +222,8 @@ EXPORT_SYMBOL_GPL(set_required_buffer_size);
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* @method_id: Method ID to call
* &in: Buffer containing input for the method call
* &out: Empty buffer to return the method results
* @in: Buffer containing input for the method call
* @out: Empty buffer to return the method results
*
* Call an ACPI-WMI method
*/
@@ -244,8 +244,8 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
* @wdev: A wmi bus device from a driver
* @instance: Instance index
* @method_id: Method ID to call
* &in: Buffer containing input for the method call
* &out: Empty buffer to return the method results
* @in: Buffer containing input for the method call
* @out: Empty buffer to return the method results
*
* Call an ACPI-WMI method
*/
@@ -364,7 +364,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
* wmi_query_block - Return contents of a WMI block (deprecated)
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* &out: Empty buffer to return the contents of the data block to
* @out: Empty buffer to return the contents of the data block to
*
* Return the contents of an ACPI-WMI data block to a buffer
*/
@@ -399,7 +399,7 @@ EXPORT_SYMBOL_GPL(wmidev_block_query);
* wmi_set_block - Write to a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* &in: Buffer containing new values for the data block
* @in: Buffer containing new values for the data block
*
* Write the contents of the input buffer to an ACPI-WMI data block
*/
@@ -510,6 +510,7 @@ static void wmi_notify_debug(u32 value, void *context)
/**
* wmi_install_notify_handler - Register handler for WMI events
* @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @handler: Function to handle notifications
* @data: Data to be returned to handler when event is fired
*
@@ -520,12 +521,12 @@ wmi_notify_handler handler, void *data)
{
struct wmi_block *block;
acpi_status status = AE_NOT_EXIST;
uuid_le guid_input;
guid_t guid_input;
if (!guid || !handler)
return AE_BAD_PARAMETER;
if (uuid_le_to_bin(guid, &guid_input))
if (guid_parse(guid, &guid_input))
return AE_BAD_PARAMETER;
list_for_each_entry(block, &wmi_block_list, list) {
@@ -552,6 +553,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
/**
* wmi_uninstall_notify_handler - Unregister handler for WMI events
* @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
*
* Unregister handler for events sent to the ACPI-WMI mapper device.
*/
@@ -559,12 +561,12 @@ acpi_status wmi_remove_notify_handler(const char *guid)
{
struct wmi_block *block;
acpi_status status = AE_NOT_EXIST;
uuid_le guid_input;
guid_t guid_input;
if (!guid)
return AE_BAD_PARAMETER;
if (uuid_le_to_bin(guid, &guid_input))
if (guid_parse(guid, &guid_input))
return AE_BAD_PARAMETER;
list_for_each_entry(block, &wmi_block_list, list) {
@@ -795,9 +797,9 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
return 0;
while (*id->guid_string) {
uuid_le driver_guid;
guid_t driver_guid;
if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid)))
if (WARN_ON(guid_parse(id->guid_string, &driver_guid)))
continue;
if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
return 1;
@@ -1116,8 +1118,7 @@ static void wmi_free_devices(struct acpi_device *device)
}
}
static bool guid_already_parsed(struct acpi_device *device,
const u8 *guid)
static bool guid_already_parsed(struct acpi_device *device, const u8 *guid)
{
struct wmi_block *wblock;
@@ -1327,10 +1328,8 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
wblock->handler(event, wblock->handler_data);
}
if (debug_event) {
pr_info("DEBUG Event GUID: %pUL\n",
wblock->gblock.guid);
}
if (debug_event)
pr_info("DEBUG Event GUID: %pUL\n", wblock->gblock.guid);
acpi_bus_generate_netlink_event(
wblock->acpi_device->pnp.device_class,