Merge branches 'acpi-apei', 'acpi-x86', 'acpi-battery' and 'acpi-pfrut'
Make ACPI APEI updates, x86-specific ACPI updates, ACPI battery driver fix and ACPI PFRU/T driver fixes for 6.2-rc1: - Drop unsetting ACPI APEI driver data on remove (Uwe Kleine-König). - Use xchg_release() instead of cmpxchg() for updating new GHES cache slots (Ard Biesheuvel). - Clean up the ACPI APEI code (Sudeep Holla, Christophe JAILLET, Jay Lu). - Add new I2C device enumeration quirks for Medion Lifetab S10346 and Lenovo Yoga Tab 3 Pro (YT3-X90F) (Hans de Goede). - Make the ACPI battery driver notify user space about adding new battery hooks and removing the existing ones (Armin Wolf). - Modify the pfr_update and pfr_telemetry drivers to use ACPI_FREE() for freeing acpi_object structures to help diagnostics (Wang ShaoBo). * acpi-apei: ACPI: APEI: EINJ: Refactor available_error_type_show() ACPI: APEI: EINJ: Fix formatting errors ACPI: APEI: Remove a useless include ACPI: APEI: Silence missing prototype warnings apei/ghes: Use xchg_release() for updating new cache slot instead of cmpxchg() ACPI: APEI: Drop unsetting driver data on remove * acpi-x86: ACPI: x86: Add skip i2c clients quirk for Medion Lifetab S10346 ACPI: x86: Add skip i2c clients quirk for Lenovo Yoga Tab 3 Pro (YT3-X90F) * acpi-battery: ACPI: battery: Call power_supply_changed() when adding hooks * acpi-pfrut: ACPI: pfr_update: use ACPI_FREE() to free acpi_object ACPI: pfr_telemetry: use ACPI_FREE() to free acpi_object
This commit is contained in:
commit
39f26d1012
@ -25,9 +25,9 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kref.h>
|
#include <linux/kref.h>
|
||||||
#include <linux/rculist.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
#include <acpi/apei.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#include "apei-internal.h"
|
#include "apei-internal.h"
|
||||||
|
@ -358,6 +358,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
|
|||||||
*/
|
*/
|
||||||
if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
|
if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
|
||||||
struct apei_resources addr_resources;
|
struct apei_resources addr_resources;
|
||||||
|
|
||||||
apei_resources_init(&addr_resources);
|
apei_resources_init(&addr_resources);
|
||||||
trigger_param_region = einj_get_trigger_parameter_region(
|
trigger_param_region = einj_get_trigger_parameter_region(
|
||||||
trigger_tab, param1, param2);
|
trigger_tab, param1, param2);
|
||||||
@ -432,11 +433,11 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
|||||||
}
|
}
|
||||||
v5param->flags = vendor_flags;
|
v5param->flags = vendor_flags;
|
||||||
} else if (flags) {
|
} else if (flags) {
|
||||||
v5param->flags = flags;
|
v5param->flags = flags;
|
||||||
v5param->memory_address = param1;
|
v5param->memory_address = param1;
|
||||||
v5param->memory_address_range = param2;
|
v5param->memory_address_range = param2;
|
||||||
v5param->apicid = param3;
|
v5param->apicid = param3;
|
||||||
v5param->pcie_sbdf = param4;
|
v5param->pcie_sbdf = param4;
|
||||||
} else {
|
} else {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ACPI_EINJ_PROCESSOR_CORRECTABLE:
|
case ACPI_EINJ_PROCESSOR_CORRECTABLE:
|
||||||
@ -466,6 +467,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
|||||||
return rc;
|
return rc;
|
||||||
if (einj_param) {
|
if (einj_param) {
|
||||||
struct einj_parameter *v4param = einj_param;
|
struct einj_parameter *v4param = einj_param;
|
||||||
|
|
||||||
v4param->param1 = param1;
|
v4param->param1 = param1;
|
||||||
v4param->param2 = param2;
|
v4param->param2 = param2;
|
||||||
}
|
}
|
||||||
@ -569,6 +571,20 @@ static u64 error_param2;
|
|||||||
static u64 error_param3;
|
static u64 error_param3;
|
||||||
static u64 error_param4;
|
static u64 error_param4;
|
||||||
static struct dentry *einj_debug_dir;
|
static struct dentry *einj_debug_dir;
|
||||||
|
static const char * const einj_error_type_string[] = {
|
||||||
|
"0x00000001\tProcessor Correctable\n",
|
||||||
|
"0x00000002\tProcessor Uncorrectable non-fatal\n",
|
||||||
|
"0x00000004\tProcessor Uncorrectable fatal\n",
|
||||||
|
"0x00000008\tMemory Correctable\n",
|
||||||
|
"0x00000010\tMemory Uncorrectable non-fatal\n",
|
||||||
|
"0x00000020\tMemory Uncorrectable fatal\n",
|
||||||
|
"0x00000040\tPCI Express Correctable\n",
|
||||||
|
"0x00000080\tPCI Express Uncorrectable non-fatal\n",
|
||||||
|
"0x00000100\tPCI Express Uncorrectable fatal\n",
|
||||||
|
"0x00000200\tPlatform Correctable\n",
|
||||||
|
"0x00000400\tPlatform Uncorrectable non-fatal\n",
|
||||||
|
"0x00000800\tPlatform Uncorrectable fatal\n",
|
||||||
|
};
|
||||||
|
|
||||||
static int available_error_type_show(struct seq_file *m, void *v)
|
static int available_error_type_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
@ -578,30 +594,9 @@ static int available_error_type_show(struct seq_file *m, void *v)
|
|||||||
rc = einj_get_available_error_type(&available_error_type);
|
rc = einj_get_available_error_type(&available_error_type);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
if (available_error_type & 0x0001)
|
for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
|
||||||
seq_printf(m, "0x00000001\tProcessor Correctable\n");
|
if (available_error_type & BIT(pos))
|
||||||
if (available_error_type & 0x0002)
|
seq_puts(m, einj_error_type_string[pos]);
|
||||||
seq_printf(m, "0x00000002\tProcessor Uncorrectable non-fatal\n");
|
|
||||||
if (available_error_type & 0x0004)
|
|
||||||
seq_printf(m, "0x00000004\tProcessor Uncorrectable fatal\n");
|
|
||||||
if (available_error_type & 0x0008)
|
|
||||||
seq_printf(m, "0x00000008\tMemory Correctable\n");
|
|
||||||
if (available_error_type & 0x0010)
|
|
||||||
seq_printf(m, "0x00000010\tMemory Uncorrectable non-fatal\n");
|
|
||||||
if (available_error_type & 0x0020)
|
|
||||||
seq_printf(m, "0x00000020\tMemory Uncorrectable fatal\n");
|
|
||||||
if (available_error_type & 0x0040)
|
|
||||||
seq_printf(m, "0x00000040\tPCI Express Correctable\n");
|
|
||||||
if (available_error_type & 0x0080)
|
|
||||||
seq_printf(m, "0x00000080\tPCI Express Uncorrectable non-fatal\n");
|
|
||||||
if (available_error_type & 0x0100)
|
|
||||||
seq_printf(m, "0x00000100\tPCI Express Uncorrectable fatal\n");
|
|
||||||
if (available_error_type & 0x0200)
|
|
||||||
seq_printf(m, "0x00000200\tPlatform Correctable\n");
|
|
||||||
if (available_error_type & 0x0400)
|
|
||||||
seq_printf(m, "0x00000400\tPlatform Uncorrectable non-fatal\n");
|
|
||||||
if (available_error_type & 0x0800)
|
|
||||||
seq_printf(m, "0x00000800\tPlatform Uncorrectable fatal\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -689,8 +684,7 @@ static int __init einj_init(void)
|
|||||||
if (status == AE_NOT_FOUND) {
|
if (status == AE_NOT_FOUND) {
|
||||||
pr_warn("EINJ table not found.\n");
|
pr_warn("EINJ table not found.\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
} else if (ACPI_FAILURE(status)) {
|
||||||
else if (ACPI_FAILURE(status)) {
|
|
||||||
pr_err("Failed to get EINJ table: %s\n",
|
pr_err("Failed to get EINJ table: %s\n",
|
||||||
acpi_format_exception(status));
|
acpi_format_exception(status));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -138,7 +138,7 @@ struct ghes_vendor_record_entry {
|
|||||||
static struct gen_pool *ghes_estatus_pool;
|
static struct gen_pool *ghes_estatus_pool;
|
||||||
static unsigned long ghes_estatus_pool_size_request;
|
static unsigned long ghes_estatus_pool_size_request;
|
||||||
|
|
||||||
static struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
|
static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
|
||||||
static atomic_t ghes_estatus_cache_alloced;
|
static atomic_t ghes_estatus_cache_alloced;
|
||||||
|
|
||||||
static int ghes_panic_timeout __read_mostly = 30;
|
static int ghes_panic_timeout __read_mostly = 30;
|
||||||
@ -773,48 +773,42 @@ static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
|
|||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
|
static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
|
||||||
{
|
{
|
||||||
|
struct ghes_estatus_cache *cache;
|
||||||
u32 len;
|
u32 len;
|
||||||
|
|
||||||
|
cache = container_of(head, struct ghes_estatus_cache, rcu);
|
||||||
len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
|
len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
|
||||||
len = GHES_ESTATUS_CACHE_LEN(len);
|
len = GHES_ESTATUS_CACHE_LEN(len);
|
||||||
gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
|
gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
|
||||||
atomic_dec(&ghes_estatus_cache_alloced);
|
atomic_dec(&ghes_estatus_cache_alloced);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
|
static void
|
||||||
|
ghes_estatus_cache_add(struct acpi_hest_generic *generic,
|
||||||
|
struct acpi_hest_generic_status *estatus)
|
||||||
{
|
{
|
||||||
struct ghes_estatus_cache *cache;
|
|
||||||
|
|
||||||
cache = container_of(head, struct ghes_estatus_cache, rcu);
|
|
||||||
ghes_estatus_cache_free(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ghes_estatus_cache_add(
|
|
||||||
struct acpi_hest_generic *generic,
|
|
||||||
struct acpi_hest_generic_status *estatus)
|
|
||||||
{
|
|
||||||
int i, slot = -1, count;
|
|
||||||
unsigned long long now, duration, period, max_period = 0;
|
unsigned long long now, duration, period, max_period = 0;
|
||||||
struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
|
struct ghes_estatus_cache *cache, *new_cache;
|
||||||
|
struct ghes_estatus_cache __rcu *victim;
|
||||||
|
int i, slot = -1, count;
|
||||||
|
|
||||||
new_cache = ghes_estatus_cache_alloc(generic, estatus);
|
new_cache = ghes_estatus_cache_alloc(generic, estatus);
|
||||||
if (new_cache == NULL)
|
if (!new_cache)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
now = sched_clock();
|
now = sched_clock();
|
||||||
for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
|
for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
|
||||||
cache = rcu_dereference(ghes_estatus_caches[i]);
|
cache = rcu_dereference(ghes_estatus_caches[i]);
|
||||||
if (cache == NULL) {
|
if (cache == NULL) {
|
||||||
slot = i;
|
slot = i;
|
||||||
slot_cache = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
duration = now - cache->time_in;
|
duration = now - cache->time_in;
|
||||||
if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
|
if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
|
||||||
slot = i;
|
slot = i;
|
||||||
slot_cache = cache;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
count = atomic_read(&cache->count);
|
count = atomic_read(&cache->count);
|
||||||
@ -823,18 +817,30 @@ static void ghes_estatus_cache_add(
|
|||||||
if (period > max_period) {
|
if (period > max_period) {
|
||||||
max_period = period;
|
max_period = period;
|
||||||
slot = i;
|
slot = i;
|
||||||
slot_cache = cache;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* new_cache must be put into array after its contents are written */
|
|
||||||
smp_wmb();
|
|
||||||
if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
|
|
||||||
slot_cache, new_cache) == slot_cache) {
|
|
||||||
if (slot_cache)
|
|
||||||
call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
|
|
||||||
} else
|
|
||||||
ghes_estatus_cache_free(new_cache);
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (slot != -1) {
|
||||||
|
/*
|
||||||
|
* Use release semantics to ensure that ghes_estatus_cached()
|
||||||
|
* running on another CPU will see the updated cache fields if
|
||||||
|
* it can see the new value of the pointer.
|
||||||
|
*/
|
||||||
|
victim = xchg_release(&ghes_estatus_caches[slot],
|
||||||
|
RCU_INITIALIZER(new_cache));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, victim may point to a cached item different
|
||||||
|
* from the one based on which we selected the slot. Instead of
|
||||||
|
* going to the loop again to pick another slot, let's just
|
||||||
|
* drop the other item anyway: this may cause a false cache
|
||||||
|
* miss later on, but that won't cause any problems.
|
||||||
|
*/
|
||||||
|
if (victim)
|
||||||
|
call_rcu(&unrcu_pointer(victim)->rcu,
|
||||||
|
ghes_estatus_cache_rcu_free);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __ghes_panic(struct ghes *ghes,
|
static void __ghes_panic(struct ghes *ghes,
|
||||||
@ -1444,8 +1450,6 @@ static int ghes_remove(struct platform_device *ghes_dev)
|
|||||||
|
|
||||||
kfree(ghes);
|
kfree(ghes);
|
||||||
|
|
||||||
platform_set_drvdata(ghes_dev, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +696,8 @@ static void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock)
|
|||||||
if (lock)
|
if (lock)
|
||||||
mutex_lock(&hook_mutex);
|
mutex_lock(&hook_mutex);
|
||||||
list_for_each_entry(battery, &acpi_battery_list, list) {
|
list_for_each_entry(battery, &acpi_battery_list, list) {
|
||||||
hook->remove_battery(battery->bat);
|
if (!hook->remove_battery(battery->bat))
|
||||||
|
power_supply_changed(battery->bat);
|
||||||
}
|
}
|
||||||
list_del(&hook->list);
|
list_del(&hook->list);
|
||||||
if (lock)
|
if (lock)
|
||||||
@ -735,6 +736,8 @@ void battery_hook_register(struct acpi_battery_hook *hook)
|
|||||||
__battery_hook_unregister(hook, 0);
|
__battery_hook_unregister(hook, 0);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
power_supply_changed(battery->bat);
|
||||||
}
|
}
|
||||||
pr_info("new extension: %s\n", hook->name);
|
pr_info("new extension: %s\n", hook->name);
|
||||||
end:
|
end:
|
||||||
|
@ -144,7 +144,7 @@ static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info,
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
free_acpi_buffer:
|
free_acpi_buffer:
|
||||||
kfree(out_obj);
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev)
|
|||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(out_obj);
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -218,7 +218,7 @@ static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev)
|
|||||||
ret = obj->integer.value;
|
ret = obj->integer.value;
|
||||||
|
|
||||||
free_acpi_buffer:
|
free_acpi_buffer:
|
||||||
kfree(out_obj);
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
free_acpi_buffer:
|
free_acpi_buffer:
|
||||||
kfree(out_obj);
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -224,7 +224,7 @@ static int query_buffer(struct pfru_com_buf_info *info,
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
free_acpi_buffer:
|
free_acpi_buffer:
|
||||||
kfree(out_obj);
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -385,7 +385,7 @@ static int start_update(int action, struct pfru_device *pfru_dev)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
free_acpi_buffer:
|
free_acpi_buffer:
|
||||||
kfree(out_obj);
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -308,7 +308,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
|
|||||||
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
|
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
/* Lenovo Yoga Tablet 1050F/L */
|
/* Lenovo Yoga Tablet 2 1050F/L */
|
||||||
.matches = {
|
.matches = {
|
||||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
|
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
|
||||||
@ -319,6 +319,27 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
|
|||||||
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
|
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
|
||||||
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
|
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/* Lenovo Yoga Tab 3 Pro X90F */
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
|
||||||
|
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* Medion Lifetab S10346 */
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
|
||||||
|
/* Way too generic, also match on BIOS data */
|
||||||
|
DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
|
||||||
|
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/* Nextbook Ares 8 */
|
/* Nextbook Ares 8 */
|
||||||
.matches = {
|
.matches = {
|
||||||
@ -348,6 +369,7 @@ static const struct acpi_device_id i2c_acpi_known_good_ids[] = {
|
|||||||
{ "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
|
{ "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
|
||||||
{ "INT33F4", 0 }, /* X-Powers AXP288 PMIC */
|
{ "INT33F4", 0 }, /* X-Powers AXP288 PMIC */
|
||||||
{ "INT33FD", 0 }, /* Intel Crystal Cove PMIC */
|
{ "INT33FD", 0 }, /* Intel Crystal Cove PMIC */
|
||||||
|
{ "INT34D3", 0 }, /* Intel Whiskey Cove PMIC */
|
||||||
{ "NPCE69A", 0 }, /* Asus Transformer keyboard dock */
|
{ "NPCE69A", 0 }, /* Asus Transformer keyboard dock */
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user