2019-06-01 10:08:55 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-03-30 23:12:00 -07:00
/*
* Supports for the button array on SoC tablets originally running
* Windows 8.
*
* ( C ) Copyright 2014 Intel Corporation
*/
# include <linux/module.h>
# include <linux/input.h>
# include <linux/init.h>
2020-11-24 17:42:57 -08:00
# include <linux/irq.h>
2014-03-30 23:12:00 -07:00
# include <linux/kernel.h>
# include <linux/acpi.h>
2020-09-13 23:07:27 -07:00
# include <linux/dmi.h>
2014-03-30 23:12:00 -07:00
# include <linux/gpio/consumer.h>
# include <linux/gpio_keys.h>
2016-11-25 11:20:43 -08:00
# include <linux/gpio.h>
2014-03-30 23:12:00 -07:00
# include <linux/platform_device.h>
struct soc_button_info {
const char * name ;
int acpi_index ;
unsigned int event_type ;
unsigned int event_code ;
bool autorepeat ;
bool wakeup ;
2020-09-13 23:04:28 -07:00
bool active_low ;
2014-03-30 23:12:00 -07:00
} ;
2019-07-27 11:25:38 +03:00
struct soc_device_data {
const struct soc_button_info * button_info ;
int ( * check ) ( struct device * dev ) ;
} ;
2014-03-30 23:12:00 -07:00
/*
* Some of the buttons like volume up / down are auto repeat , while others
* are not . To support both , we register two platform devices , and put
* buttons into them based on whether the key should be auto repeat .
*/
# define BUTTON_TYPES 2
struct soc_button_data {
struct platform_device * children [ BUTTON_TYPES ] ;
} ;
2020-09-13 23:07:27 -07:00
/*
* Some 2 - in - 1 s which use the soc_button_array driver have this ugly issue in
* their DSDT where the _LID method modifies the irq - type settings of the GPIOs
* used for the power and home buttons . The intend of this AML code is to
* disable these buttons when the lid is closed .
* The AML does this by directly poking the GPIO controllers registers . This is
* problematic because when re - enabling the irq , which happens whenever _LID
* gets called with the lid open ( e . g . on boot and on resume ) , it sets the
* irq - type to IRQ_TYPE_LEVEL_LOW . Where as the gpio - keys driver programs the
* type to , and expects it to be , IRQ_TYPE_EDGE_BOTH .
* To work around this we don ' t set gpio_keys_button . gpio on these 2 - in - 1 s ,
* instead we get the irq for the GPIO ourselves , configure it as
* IRQ_TYPE_LEVEL_LOW ( to match how the _LID AML code configures it ) and pass
* the irq in gpio_keys_button . irq . Below is a list of affected devices .
*/
static const struct dmi_system_id dmi_use_low_level_irq [ ] = {
{
/*
* Acer Switch 10 SW5 - 012. _LID method messes with home - and
* power - button GPIO IRQ settings . When ( re - ) enabling the irq
* it ors in its own flags without clearing the previous set
* ones , leading to an irq - type of IRQ_TYPE_LEVEL_LOW |
* IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm .
*/
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire SW5-012 " ) ,
} ,
} ,
{
/*
* Acer One S1003 . _LID method messes with power - button GPIO
* IRQ settings , leading to a non working power - button .
*/
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " One S1003 " ) ,
} ,
} ,
2020-12-06 21:59:42 -08:00
{
/*
* Lenovo Yoga Tab2 1051L , something messes with the home - button
* IRQ settings , leading to a non working home - button .
*/
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " LENOVO " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " 60073 " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " 1051L " ) ,
} ,
} ,
2020-09-13 23:07:27 -07:00
{ } /* Terminating entry */
} ;
2014-03-30 23:12:00 -07:00
/*
* Get the Nth GPIO number from the ACPI object .
*/
2020-09-13 23:07:27 -07:00
static int soc_button_lookup_gpio ( struct device * dev , int acpi_index ,
int * gpio_ret , int * irq_ret )
2014-03-30 23:12:00 -07:00
{
struct gpio_desc * desc ;
2017-03-09 09:58:30 -08:00
desc = gpiod_get_index ( dev , NULL , acpi_index , GPIOD_ASIS ) ;
2014-03-30 23:12:00 -07:00
if ( IS_ERR ( desc ) )
return PTR_ERR ( desc ) ;
2020-09-13 23:07:27 -07:00
* gpio_ret = desc_to_gpio ( desc ) ;
* irq_ret = gpiod_to_irq ( desc ) ;
2014-03-30 23:12:00 -07:00
gpiod_put ( desc ) ;
2020-09-13 23:07:27 -07:00
return 0 ;
2014-03-30 23:12:00 -07:00
}
static struct platform_device *
2014-09-22 10:31:14 -07:00
soc_button_device_create ( struct platform_device * pdev ,
2014-03-30 23:12:00 -07:00
const struct soc_button_info * button_info ,
bool autorepeat )
{
const struct soc_button_info * info ;
struct platform_device * pd ;
struct gpio_keys_button * gpio_keys ;
struct gpio_keys_platform_data * gpio_keys_pdata ;
2020-09-13 23:07:27 -07:00
int error , gpio , irq ;
2014-03-30 23:12:00 -07:00
int n_buttons = 0 ;
2017-03-17 14:03:28 -07:00
for ( info = button_info ; info - > name ; info + + )
if ( info - > autorepeat = = autorepeat )
n_buttons + + ;
2014-03-30 23:12:00 -07:00
gpio_keys_pdata = devm_kzalloc ( & pdev - > dev ,
sizeof ( * gpio_keys_pdata ) +
2017-03-17 14:03:28 -07:00
sizeof ( * gpio_keys ) * n_buttons ,
2014-03-30 23:12:00 -07:00
GFP_KERNEL ) ;
2014-07-25 18:41:39 -07:00
if ( ! gpio_keys_pdata )
return ERR_PTR ( - ENOMEM ) ;
2014-03-30 23:12:00 -07:00
gpio_keys = ( void * ) ( gpio_keys_pdata + 1 ) ;
2017-03-17 14:03:28 -07:00
n_buttons = 0 ;
2014-03-30 23:12:00 -07:00
for ( info = button_info ; info - > name ; info + + ) {
if ( info - > autorepeat ! = autorepeat )
continue ;
2020-09-13 23:07:27 -07:00
error = soc_button_lookup_gpio ( & pdev - > dev , info - > acpi_index , & gpio , & irq ) ;
if ( error | | irq < 0 ) {
2019-10-08 16:40:33 -07:00
/*
* Skip GPIO if not present . Note we deliberately
* ignore - EPROBE_DEFER errors here . On some devices
* Intel is using so called virtual GPIOs which are not
* GPIOs at all but some way for AML code to check some
* random status bits without need a custom opregion .
* In some cases the resources table we parse points to
* such a virtual GPIO , since these are not real GPIOs
* we do not have a driver for these so they will never
* show up , therefore we ignore - EPROBE_DEFER .
*/
2014-03-30 23:12:00 -07:00
continue ;
2019-07-27 11:25:38 +03:00
}
2014-03-30 23:12:00 -07:00
2020-09-13 23:07:27 -07:00
/* See dmi_use_low_level_irq[] comment */
if ( ! autorepeat & & dmi_check_system ( dmi_use_low_level_irq ) ) {
irq_set_irq_type ( irq , IRQ_TYPE_LEVEL_LOW ) ;
gpio_keys [ n_buttons ] . irq = irq ;
gpio_keys [ n_buttons ] . gpio = - ENOENT ;
} else {
gpio_keys [ n_buttons ] . gpio = gpio ;
}
2014-03-30 23:12:00 -07:00
gpio_keys [ n_buttons ] . type = info - > event_type ;
gpio_keys [ n_buttons ] . code = info - > event_code ;
2020-09-13 23:04:28 -07:00
gpio_keys [ n_buttons ] . active_low = info - > active_low ;
2014-03-30 23:12:00 -07:00
gpio_keys [ n_buttons ] . desc = info - > name ;
gpio_keys [ n_buttons ] . wakeup = info - > wakeup ;
2017-01-21 11:14:14 -08:00
/* These devices often use cheap buttons, use 50 ms debounce */
gpio_keys [ n_buttons ] . debounce_interval = 50 ;
2014-03-30 23:12:00 -07:00
n_buttons + + ;
}
if ( n_buttons = = 0 ) {
error = - ENODEV ;
goto err_free_mem ;
}
gpio_keys_pdata - > buttons = gpio_keys ;
gpio_keys_pdata - > nbuttons = n_buttons ;
gpio_keys_pdata - > rep = autorepeat ;
2019-08-20 12:08:43 -07:00
pd = platform_device_register_resndata ( & pdev - > dev , " gpio-keys " ,
PLATFORM_DEVID_AUTO , NULL , 0 ,
gpio_keys_pdata ,
sizeof ( * gpio_keys_pdata ) ) ;
error = PTR_ERR_OR_ZERO ( pd ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" failed registering gpio-keys: %d \n " , error ) ;
2014-03-30 23:12:00 -07:00
goto err_free_mem ;
}
return pd ;
err_free_mem :
devm_kfree ( & pdev - > dev , gpio_keys_pdata ) ;
return ERR_PTR ( error ) ;
}
2017-03-17 14:03:54 -07:00
static int soc_button_get_acpi_object_int ( const union acpi_object * obj )
{
if ( obj - > type ! = ACPI_TYPE_INTEGER )
return - 1 ;
return obj - > integer . value ;
}
/* Parse a single ACPI0011 _DSD button descriptor */
static int soc_button_parse_btn_desc ( struct device * dev ,
const union acpi_object * desc ,
int collection_uid ,
struct soc_button_info * info )
{
int upage , usage ;
if ( desc - > type ! = ACPI_TYPE_PACKAGE | |
desc - > package . count ! = 5 | |
/* First byte should be 1 (control) */
soc_button_get_acpi_object_int ( & desc - > package . elements [ 0 ] ) ! = 1 | |
/* Third byte should be collection uid */
soc_button_get_acpi_object_int ( & desc - > package . elements [ 2 ] ) ! =
collection_uid ) {
dev_err ( dev , " Invalid ACPI Button Descriptor \n " ) ;
return - ENODEV ;
}
info - > event_type = EV_KEY ;
2020-09-13 23:04:28 -07:00
info - > active_low = true ;
2017-03-17 14:03:54 -07:00
info - > acpi_index =
soc_button_get_acpi_object_int ( & desc - > package . elements [ 1 ] ) ;
upage = soc_button_get_acpi_object_int ( & desc - > package . elements [ 3 ] ) ;
usage = soc_button_get_acpi_object_int ( & desc - > package . elements [ 4 ] ) ;
/*
* The UUID : fa6bd625 - 9 ce8 - 470 d - a2c7 - b3ca36c4282e descriptors use HID
* usage page and usage codes , but otherwise the device is not HID
* compliant : it uses one irq per button instead of generating HID
* input reports and some buttons should generate wakeups where as
* others should not , so we cannot use the HID subsystem .
*
* Luckily all devices only use a few usage page + usage combinations ,
* so we can simply check for the known combinations here .
*/
if ( upage = = 0x01 & & usage = = 0x81 ) {
info - > name = " power " ;
info - > event_code = KEY_POWER ;
info - > wakeup = true ;
2019-01-03 18:09:39 -08:00
} else if ( upage = = 0x01 & & usage = = 0xca ) {
info - > name = " rotation lock switch " ;
info - > event_type = EV_SW ;
info - > event_code = SW_ROTATE_LOCK ;
2017-03-17 14:03:54 -07:00
} else if ( upage = = 0x07 & & usage = = 0xe3 ) {
info - > name = " home " ;
2017-04-10 20:34:19 -07:00
info - > event_code = KEY_LEFTMETA ;
2017-03-17 14:03:54 -07:00
info - > wakeup = true ;
} else if ( upage = = 0x0c & & usage = = 0xe9 ) {
info - > name = " volume_up " ;
info - > event_code = KEY_VOLUMEUP ;
info - > autorepeat = true ;
} else if ( upage = = 0x0c & & usage = = 0xea ) {
info - > name = " volume_down " ;
info - > event_code = KEY_VOLUMEDOWN ;
info - > autorepeat = true ;
} else {
dev_warn ( dev , " Unknown button index %d upage %02x usage %02x, ignoring \n " ,
info - > acpi_index , upage , usage ) ;
info - > name = " unknown " ;
info - > event_code = KEY_RESERVED ;
}
return 0 ;
}
/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */
static const u8 btns_desc_uuid [ 16 ] = {
0x25 , 0xd6 , 0x6b , 0xfa , 0xe8 , 0x9c , 0x0d , 0x47 ,
0xa2 , 0xc7 , 0xb3 , 0xca , 0x36 , 0xc4 , 0x28 , 0x2e
} ;
/* Parse ACPI0011 _DSD button descriptors */
static struct soc_button_info * soc_button_get_button_info ( struct device * dev )
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER } ;
const union acpi_object * desc , * el0 , * uuid , * btns_desc = NULL ;
struct soc_button_info * button_info ;
acpi_status status ;
int i , btn , collection_uid = - 1 ;
status = acpi_evaluate_object_typed ( ACPI_HANDLE ( dev ) , " _DSD " , NULL ,
& buf , ACPI_TYPE_PACKAGE ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( dev , " ACPI _DSD object not found \n " ) ;
return ERR_PTR ( - ENODEV ) ;
}
/* Look for the Button Descriptors UUID */
desc = buf . pointer ;
for ( i = 0 ; ( i + 1 ) < desc - > package . count ; i + = 2 ) {
uuid = & desc - > package . elements [ i ] ;
if ( uuid - > type ! = ACPI_TYPE_BUFFER | |
uuid - > buffer . length ! = 16 | |
desc - > package . elements [ i + 1 ] . type ! = ACPI_TYPE_PACKAGE ) {
break ;
}
if ( memcmp ( uuid - > buffer . pointer , btns_desc_uuid , 16 ) = = 0 ) {
btns_desc = & desc - > package . elements [ i + 1 ] ;
break ;
}
}
if ( ! btns_desc ) {
dev_err ( dev , " ACPI Button Descriptors not found \n " ) ;
2017-06-18 15:10:26 -07:00
button_info = ERR_PTR ( - ENODEV ) ;
goto out ;
2017-03-17 14:03:54 -07:00
}
/* The first package describes the collection */
el0 = & btns_desc - > package . elements [ 0 ] ;
if ( el0 - > type = = ACPI_TYPE_PACKAGE & &
el0 - > package . count = = 5 & &
/* First byte should be 0 (collection) */
soc_button_get_acpi_object_int ( & el0 - > package . elements [ 0 ] ) = = 0 & &
/* Third byte should be 0 (top level collection) */
soc_button_get_acpi_object_int ( & el0 - > package . elements [ 2 ] ) = = 0 ) {
collection_uid = soc_button_get_acpi_object_int (
& el0 - > package . elements [ 1 ] ) ;
}
if ( collection_uid = = - 1 ) {
dev_err ( dev , " Invalid Button Collection Descriptor \n " ) ;
2017-06-18 15:10:26 -07:00
button_info = ERR_PTR ( - ENODEV ) ;
goto out ;
2017-03-17 14:03:54 -07:00
}
/* There are package.count - 1 buttons + 1 terminating empty entry */
button_info = devm_kcalloc ( dev , btns_desc - > package . count ,
sizeof ( * button_info ) , GFP_KERNEL ) ;
2017-06-18 15:10:26 -07:00
if ( ! button_info ) {
button_info = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
2017-03-17 14:03:54 -07:00
/* Parse the button descriptors */
for ( i = 1 , btn = 0 ; i < btns_desc - > package . count ; i + + , btn + + ) {
if ( soc_button_parse_btn_desc ( dev ,
& btns_desc - > package . elements [ i ] ,
collection_uid ,
2017-06-18 15:10:26 -07:00
& button_info [ btn ] ) ) {
button_info = ERR_PTR ( - ENODEV ) ;
goto out ;
}
2017-03-17 14:03:54 -07:00
}
2017-06-18 15:10:26 -07:00
out :
kfree ( buf . pointer ) ;
2017-03-17 14:03:54 -07:00
return button_info ;
}
2014-09-22 10:31:14 -07:00
static int soc_button_remove ( struct platform_device * pdev )
2014-03-30 23:12:00 -07:00
{
2014-09-22 10:31:14 -07:00
struct soc_button_data * priv = platform_get_drvdata ( pdev ) ;
2014-03-30 23:12:00 -07:00
int i ;
for ( i = 0 ; i < BUTTON_TYPES ; i + + )
if ( priv - > children [ i ] )
platform_device_unregister ( priv - > children [ i ] ) ;
2014-09-22 10:31:14 -07:00
return 0 ;
2014-03-30 23:12:00 -07:00
}
2014-09-22 10:31:14 -07:00
static int soc_button_probe ( struct platform_device * pdev )
2014-03-30 23:12:00 -07:00
{
2014-09-22 10:31:14 -07:00
struct device * dev = & pdev - > dev ;
2019-07-27 11:25:38 +03:00
const struct soc_device_data * device_data ;
const struct soc_button_info * button_info ;
2014-03-30 23:12:00 -07:00
struct soc_button_data * priv ;
struct platform_device * pd ;
int i ;
int error ;
2019-07-27 11:25:38 +03:00
device_data = acpi_device_get_match_data ( dev ) ;
if ( device_data & & device_data - > check ) {
error = device_data - > check ( dev ) ;
if ( error )
return error ;
}
2014-09-22 10:31:14 -07:00
2019-07-27 11:25:38 +03:00
if ( device_data & & device_data - > button_info ) {
button_info = device_data - > button_info ;
} else {
2017-03-17 14:03:54 -07:00
button_info = soc_button_get_button_info ( dev ) ;
if ( IS_ERR ( button_info ) )
return PTR_ERR ( button_info ) ;
}
2014-09-22 10:31:14 -07:00
2017-02-20 18:15:49 +02:00
error = gpiod_count ( dev , NULL ) ;
if ( error < 0 ) {
2017-01-18 11:41:51 -08:00
dev_dbg ( dev , " no GPIO attached, ignoring... \n " ) ;
2017-08-20 09:29:03 -07:00
return - ENODEV ;
2016-11-25 11:36:01 -08:00
}
2017-01-18 11:41:51 -08:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2014-03-30 23:12:00 -07:00
if ( ! priv )
return - ENOMEM ;
2014-09-22 10:31:14 -07:00
platform_set_drvdata ( pdev , priv ) ;
2014-03-30 23:12:00 -07:00
for ( i = 0 ; i < BUTTON_TYPES ; i + + ) {
pd = soc_button_device_create ( pdev , button_info , i = = 0 ) ;
if ( IS_ERR ( pd ) ) {
error = PTR_ERR ( pd ) ;
if ( error ! = - ENODEV ) {
soc_button_remove ( pdev ) ;
return error ;
}
2014-04-22 22:47:13 -07:00
continue ;
2014-03-30 23:12:00 -07:00
}
priv - > children [ i ] = pd ;
}
if ( ! priv - > children [ 0 ] & & ! priv - > children [ 1 ] )
return - ENODEV ;
2019-07-27 11:25:38 +03:00
if ( ! device_data | | ! device_data - > button_info )
2017-03-17 14:03:54 -07:00
devm_kfree ( dev , button_info ) ;
2014-03-30 23:12:00 -07:00
return 0 ;
}
2017-03-17 14:03:28 -07:00
/*
* Definition of buttons on the tablet . The ACPI index of each button
* is defined in section 2.8 .7 .2 of " Windows ACPI Design Guide for SoC
* Platforms "
*/
2019-07-27 11:25:38 +03:00
static const struct soc_button_info soc_button_PNP0C40 [ ] = {
2020-09-13 23:04:28 -07:00
{ " power " , 0 , EV_KEY , KEY_POWER , false , true , true } ,
{ " home " , 1 , EV_KEY , KEY_LEFTMETA , false , true , true } ,
{ " volume_up " , 2 , EV_KEY , KEY_VOLUMEUP , true , false , true } ,
{ " volume_down " , 3 , EV_KEY , KEY_VOLUMEDOWN , true , false , true } ,
{ " rotation_lock " , 4 , EV_KEY , KEY_ROTATE_LOCK_TOGGLE , false , false , true } ,
2014-03-30 23:12:00 -07:00
{ }
} ;
2019-07-27 11:25:38 +03:00
static const struct soc_device_data soc_device_PNP0C40 = {
. button_info = soc_button_PNP0C40 ,
} ;
2020-09-13 23:05:19 -07:00
static const struct soc_button_info soc_button_INT33D3 [ ] = {
{ " tablet_mode " , 0 , EV_SW , SW_TABLET_MODE , false , false , false } ,
{ }
} ;
static const struct soc_device_data soc_device_INT33D3 = {
. button_info = soc_button_INT33D3 ,
} ;
2019-07-27 11:25:38 +03:00
/*
* Special device check for Surface Book 2 and Surface Pro ( 2017 ) .
* Both , the Surface Pro 4 ( surfacepro3_button . c ) and the above mentioned
* devices use MSHW0040 for power and volume buttons , however the way they
* have to be addressed differs . Make sure that we only load this drivers
* for the correct devices by checking the OEM Platform Revision provided by
* the _DSM method .
*/
# define MSHW0040_DSM_REVISION 0x01
# define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision
static const guid_t MSHW0040_DSM_UUID =
GUID_INIT ( 0x6fd05c69 , 0xcde3 , 0x49f4 , 0x95 , 0xed , 0xab , 0x16 , 0x65 ,
0x49 , 0x80 , 0x35 ) ;
static int soc_device_check_MSHW0040 ( struct device * dev )
{
acpi_handle handle = ACPI_HANDLE ( dev ) ;
union acpi_object * result ;
u64 oem_platform_rev = 0 ; // valid revisions are nonzero
// get OEM platform revision
result = acpi_evaluate_dsm_typed ( handle , & MSHW0040_DSM_UUID ,
MSHW0040_DSM_REVISION ,
MSHW0040_DSM_GET_OMPR , NULL ,
ACPI_TYPE_INTEGER ) ;
if ( result ) {
oem_platform_rev = result - > integer . value ;
ACPI_FREE ( result ) ;
}
/*
* If the revision is zero here , the _DSM evaluation has failed . This
* indicates that we have a Pro 4 or Book 1 and this driver should not
* be used .
*/
if ( oem_platform_rev = = 0 )
return - ENODEV ;
dev_dbg ( dev , " OEM Platform Revision %llu \n " , oem_platform_rev ) ;
return 0 ;
}
/*
* Button infos for Microsoft Surface Book 2 and Surface Pro ( 2017 ) .
* Obtained from DSDT / testing .
*/
static const struct soc_button_info soc_button_MSHW0040 [ ] = {
2020-09-13 23:04:28 -07:00
{ " power " , 0 , EV_KEY , KEY_POWER , false , true , true } ,
{ " volume_up " , 2 , EV_KEY , KEY_VOLUMEUP , true , false , true } ,
{ " volume_down " , 4 , EV_KEY , KEY_VOLUMEDOWN , true , false , true } ,
2019-07-27 11:25:38 +03:00
{ }
} ;
static const struct soc_device_data soc_device_MSHW0040 = {
. button_info = soc_button_MSHW0040 ,
. check = soc_device_check_MSHW0040 ,
} ;
2014-09-22 10:31:14 -07:00
static const struct acpi_device_id soc_button_acpi_match [ ] = {
2019-07-27 11:25:38 +03:00
{ " PNP0C40 " , ( unsigned long ) & soc_device_PNP0C40 } ,
2020-09-13 23:05:19 -07:00
{ " INT33D3 " , ( unsigned long ) & soc_device_INT33D3 } ,
{ " ID9001 " , ( unsigned long ) & soc_device_INT33D3 } ,
2017-03-17 14:03:54 -07:00
{ " ACPI0011 " , 0 } ,
2019-07-27 11:25:38 +03:00
/* Microsoft Surface Devices (5th and 6th generation) */
{ " MSHW0040 " , ( unsigned long ) & soc_device_MSHW0040 } ,
2014-09-22 10:31:14 -07:00
{ }
2014-03-30 23:12:00 -07:00
} ;
2014-09-22 10:31:14 -07:00
MODULE_DEVICE_TABLE ( acpi , soc_button_acpi_match ) ;
static struct platform_driver soc_button_driver = {
. probe = soc_button_probe ,
2014-03-30 23:12:00 -07:00
. remove = soc_button_remove ,
2014-09-22 10:31:14 -07:00
. driver = {
. name = KBUILD_MODNAME ,
. acpi_match_table = ACPI_PTR ( soc_button_acpi_match ) ,
} ,
2014-03-30 23:12:00 -07:00
} ;
2014-09-22 10:31:14 -07:00
module_platform_driver ( soc_button_driver ) ;
2014-03-30 23:12:00 -07:00
MODULE_LICENSE ( " GPL " ) ;