2019-05-29 07:18:02 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-09-21 12:55:15 +02:00
/*
2021-06-11 10:08:57 +02:00
* 3 - axis accelerometer driver supporting many I2C Bosch - Sensortec chips
2015-09-21 12:55:15 +02:00
* Copyright ( c ) 2014 , Intel Corporation .
*/
# include <linux/device.h>
# include <linux/mod_devicetable.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/acpi.h>
# include <linux/regmap.h>
# include "bmc150-accel.h"
2021-05-23 19:00:57 +02:00
# ifdef CONFIG_ACPI
static const struct acpi_device_id bmc150_acpi_dual_accel_ids [ ] = {
{ " BOSC0200 " } ,
2021-05-23 19:00:58 +02:00
{ " DUAL250E " } ,
2021-05-23 19:00:57 +02:00
{ }
} ;
2021-05-23 19:01:01 +02:00
/*
* The DUAL250E ACPI device for 360 ° hinges type 2 - in - 1 s with 1 accelerometer
* in the display and 1 in the hinge has an ACPI - method ( DSM ) to tell the
* ACPI code about the angle between the 2 halves . This will make the ACPI
* code enable / disable the keyboard and touchpad . We need to call this to avoid
* the keyboard being disabled when the 2 - in - 1 is turned - on or resumed while
* fully folded into tablet mode ( which gets detected with a HALL - sensor ) .
* If we don ' t call this then the keyboard won ' t work even when the 2 - in - 1 is
* changed to be used in laptop mode after the power - on / resume .
*
* This DSM takes 2 angles , selected by setting aux0 to 0 or 1 , these presumably
* define the angle between the gravity vector measured by the accelerometer in
* the display ( aux0 = 0 ) resp . the base ( aux0 = 1 ) and some reference vector .
* The 2 angles get subtracted from each other so the reference vector does
* not matter and we can simply leave the second angle at 0.
*/
# define BMC150_DSM_GUID "7681541e-8827-4239-8d9d-36be7fe12542"
# define DUAL250E_SET_ANGLE_FN_INDEX 3
struct dual250e_set_angle_args {
u32 aux0 ;
u32 ang0 ;
u32 rawx ;
u32 rawy ;
u32 rawz ;
} __packed ;
static bool bmc150_acpi_set_angle_dsm ( struct i2c_client * client , u32 aux0 , u32 ang0 )
{
struct acpi_device * adev = ACPI_COMPANION ( & client - > dev ) ;
struct dual250e_set_angle_args args = {
. aux0 = aux0 ,
. ang0 = ang0 ,
} ;
union acpi_object args_obj , * obj ;
guid_t guid ;
if ( ! acpi_dev_hid_uid_match ( adev , " DUAL250E " , NULL ) )
return false ;
guid_parse ( BMC150_DSM_GUID , & guid ) ;
if ( ! acpi_check_dsm ( adev - > handle , & guid , 0 , BIT ( DUAL250E_SET_ANGLE_FN_INDEX ) ) )
return false ;
/*
* Note this triggers the following warning :
* " ACPI Warning: \ _SB.PCI0.I2C2.ACC1._DSM: Argument #4 type mismatch -
* Found [ Buffer ] , ACPI requires [ Package ] "
* This is unavoidable since the _DSM implementation expects a " naked "
* buffer , so wrapping it in a package will _not_ work .
*/
args_obj . type = ACPI_TYPE_BUFFER ;
args_obj . buffer . length = sizeof ( args ) ;
args_obj . buffer . pointer = ( u8 * ) & args ;
obj = acpi_evaluate_dsm ( adev - > handle , & guid , 0 , DUAL250E_SET_ANGLE_FN_INDEX , & args_obj ) ;
if ( ! obj ) {
dev_err ( & client - > dev , " Failed to call DSM to enable keyboard and touchpad \n " ) ;
return false ;
}
ACPI_FREE ( obj ) ;
return true ;
}
static bool bmc150_acpi_enable_keyboard ( struct i2c_client * client )
{
/*
* The EC must see a change for it to re - enable the kbd , so first
* set the angle to 270 ° ( tent / stand mode ) and then change it to
* 90 ° ( laptop mode ) .
*/
if ( ! bmc150_acpi_set_angle_dsm ( client , 0 , 270 ) )
return false ;
/* The EC needs some time to notice the angle being changed */
msleep ( 100 ) ;
return bmc150_acpi_set_angle_dsm ( client , 0 , 90 ) ;
}
static void bmc150_acpi_resume_work ( struct work_struct * work )
{
struct bmc150_accel_data * data =
container_of ( work , struct bmc150_accel_data , resume_work . work ) ;
bmc150_acpi_enable_keyboard ( data - > second_device ) ;
}
static void bmc150_acpi_resume_handler ( struct device * dev )
{
struct bmc150_accel_data * data = iio_priv ( dev_get_drvdata ( dev ) ) ;
/*
* Delay the bmc150_acpi_enable_keyboard ( ) call till after the system
* resume has completed , otherwise it will not work .
*/
schedule_delayed_work ( & data - > resume_work , msecs_to_jiffies ( 1000 ) ) ;
}
2021-05-23 19:00:57 +02:00
/*
* Some acpi_devices describe 2 accelerometers in a single ACPI device ,
* try instantiating a second i2c_client for an I2cSerialBusV2 ACPI resource
* with index 1.
*/
static void bmc150_acpi_dual_accel_probe ( struct i2c_client * client )
{
2021-05-23 19:01:00 +02:00
struct bmc150_accel_data * data = iio_priv ( i2c_get_clientdata ( client ) ) ;
2021-05-23 19:00:57 +02:00
struct acpi_device * adev = ACPI_COMPANION ( & client - > dev ) ;
2021-05-23 19:00:58 +02:00
char dev_name [ 16 ] ;
2021-05-23 19:00:57 +02:00
struct i2c_board_info board_info = {
. type = " bmc150_accel " ,
2021-05-23 19:00:58 +02:00
. dev_name = dev_name ,
2021-05-23 19:00:57 +02:00
. fwnode = client - > dev . fwnode ,
} ;
if ( acpi_match_device_ids ( adev , bmc150_acpi_dual_accel_ids ) )
return ;
2021-05-23 19:00:58 +02:00
/*
* The 2 nd accel sits in the base of 2 - in - 1 s . The suffix is static , as
* there should never be more then 1 ACPI node with 2 accelerometers .
*/
snprintf ( dev_name , sizeof ( dev_name ) , " %s:base " , acpi_device_hid ( adev ) ) ;
board_info . irq = acpi_dev_gpio_irq_get ( adev , 1 ) ;
2021-05-23 19:01:00 +02:00
data - > second_device = i2c_acpi_new_device ( & client - > dev , 1 , & board_info ) ;
2021-05-23 19:01:01 +02:00
if ( ! IS_ERR ( data - > second_device ) & & bmc150_acpi_enable_keyboard ( data - > second_device ) ) {
INIT_DELAYED_WORK ( & data - > resume_work , bmc150_acpi_resume_work ) ;
data - > resume_callback = bmc150_acpi_resume_handler ;
}
2021-05-23 19:00:57 +02:00
}
static void bmc150_acpi_dual_accel_remove ( struct i2c_client * client )
{
2021-05-23 19:01:00 +02:00
struct bmc150_accel_data * data = iio_priv ( i2c_get_clientdata ( client ) ) ;
2021-05-23 19:00:57 +02:00
2021-05-23 19:01:01 +02:00
if ( data - > resume_callback )
cancel_delayed_work_sync ( & data - > resume_work ) ;
2021-05-23 19:01:00 +02:00
i2c_unregister_device ( data - > second_device ) ;
2021-05-23 19:00:57 +02:00
}
# else
static void bmc150_acpi_dual_accel_probe ( struct i2c_client * client ) { }
static void bmc150_acpi_dual_accel_remove ( struct i2c_client * client ) { }
# endif
2022-11-18 23:36:26 +01:00
static int bmc150_accel_probe ( struct i2c_client * client )
2015-09-21 12:55:15 +02:00
{
2022-11-18 23:36:26 +01:00
const struct i2c_device_id * id = i2c_client_get_device_id ( client ) ;
2015-09-21 12:55:15 +02:00
struct regmap * regmap ;
const char * name = NULL ;
2021-08-02 17:56:57 +02:00
enum bmc150_type type = BOSCH_UNKNOWN ;
2015-09-21 12:55:15 +02:00
bool block_supported =
i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) | |
i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_I2C_BLOCK ) ;
2020-11-30 15:19:53 +01:00
int ret ;
2015-09-21 12:55:15 +02:00
2016-03-29 15:21:21 +03:00
regmap = devm_regmap_init_i2c ( client , & bmc150_regmap_conf ) ;
2015-09-21 12:55:15 +02:00
if ( IS_ERR ( regmap ) ) {
dev_err ( & client - > dev , " Failed to initialize i2c regmap \n " ) ;
return PTR_ERR ( regmap ) ;
}
2021-08-02 17:56:57 +02:00
if ( id ) {
2015-09-21 12:55:15 +02:00
name = id - > name ;
2021-08-02 17:56:57 +02:00
type = id - > driver_data ;
}
2015-09-21 12:55:15 +02:00
2021-08-02 17:56:57 +02:00
ret = bmc150_accel_core_probe ( & client - > dev , regmap , client - > irq ,
type , name , block_supported ) ;
2020-11-30 15:19:53 +01:00
if ( ret )
return ret ;
/*
2021-05-23 19:00:57 +02:00
* The ! id check avoids recursion when probe ( ) gets called
* for the second client .
2020-11-30 15:19:53 +01:00
*/
2021-05-23 19:00:57 +02:00
if ( ! id & & has_acpi_companion ( & client - > dev ) )
bmc150_acpi_dual_accel_probe ( client ) ;
2020-11-30 15:19:53 +01:00
return 0 ;
2015-09-21 12:55:15 +02:00
}
2022-08-15 10:02:30 +02:00
static void bmc150_accel_remove ( struct i2c_client * client )
2015-09-21 12:55:15 +02:00
{
2021-05-23 19:00:57 +02:00
bmc150_acpi_dual_accel_remove ( client ) ;
2020-11-30 15:19:53 +01:00
2021-10-13 22:32:10 +02:00
bmc150_accel_core_remove ( & client - > dev ) ;
2015-09-21 12:55:15 +02:00
}
static const struct acpi_device_id bmc150_accel_acpi_match [ ] = {
2021-06-11 10:08:56 +02:00
{ " BMA0255 " } ,
2021-06-11 10:08:58 +02:00
{ " BMA0280 " } ,
2021-06-11 10:08:56 +02:00
{ " BMA222 " } ,
{ " BMA222E " } ,
2021-06-11 10:08:58 +02:00
{ " BMA250E " } ,
{ " BMC150A " } ,
{ " BMI055A " } ,
2017-06-19 08:22:25 +02:00
{ " BOSC0200 " } ,
2021-06-11 10:08:58 +02:00
{ " BSBA0150 " } ,
2021-05-23 19:00:58 +02:00
{ " DUAL250E " } ,
2015-09-21 12:55:15 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , bmc150_accel_acpi_match ) ;
static const struct i2c_device_id bmc150_accel_id [ ] = {
2021-06-11 10:08:56 +02:00
{ " bma222 " } ,
{ " bma222e " } ,
2021-06-11 10:08:58 +02:00
{ " bma250e " } ,
2021-06-11 10:09:00 +02:00
{ " bma253 " } ,
2021-06-11 10:09:03 +02:00
{ " bma254 " } ,
2021-06-11 10:08:58 +02:00
{ " bma255 " } ,
2021-06-11 10:08:56 +02:00
{ " bma280 " } ,
2021-06-11 10:08:58 +02:00
{ " bmc150_accel " } ,
2021-08-02 17:56:57 +02:00
{ " bmc156_accel " , BOSCH_BMC156 } ,
2021-06-11 10:08:58 +02:00
{ " bmi055_accel " } ,
2015-09-21 12:55:15 +02:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , bmc150_accel_id ) ;
2017-12-01 12:10:58 +01:00
static const struct of_device_id bmc150_accel_of_match [ ] = {
2020-11-15 21:57:44 +01:00
{ . compatible = " bosch,bma222 " } ,
2017-12-01 12:10:58 +01:00
{ . compatible = " bosch,bma222e " } ,
2021-06-11 10:08:58 +02:00
{ . compatible = " bosch,bma250e " } ,
2021-06-11 10:09:00 +02:00
{ . compatible = " bosch,bma253 " } ,
2021-06-11 10:09:03 +02:00
{ . compatible = " bosch,bma254 " } ,
2021-06-11 10:08:58 +02:00
{ . compatible = " bosch,bma255 " } ,
2017-12-01 12:10:58 +01:00
{ . compatible = " bosch,bma280 " } ,
2021-06-11 10:08:58 +02:00
{ . compatible = " bosch,bmc150_accel " } ,
2021-08-02 17:56:57 +02:00
{ . compatible = " bosch,bmc156_accel " } ,
2021-06-11 10:08:58 +02:00
{ . compatible = " bosch,bmi055_accel " } ,
2017-12-01 12:10:58 +01:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , bmc150_accel_of_match ) ;
2015-09-21 12:55:15 +02:00
static struct i2c_driver bmc150_accel_driver = {
. driver = {
. name = " bmc150_accel_i2c " ,
2017-12-01 12:10:58 +01:00
. of_match_table = bmc150_accel_of_match ,
2015-09-21 12:55:15 +02:00
. acpi_match_table = ACPI_PTR ( bmc150_accel_acpi_match ) ,
. pm = & bmc150_accel_pm_ops ,
} ,
2022-11-18 23:36:26 +01:00
. probe_new = bmc150_accel_probe ,
2015-09-21 12:55:15 +02:00
. remove = bmc150_accel_remove ,
. id_table = bmc150_accel_id ,
} ;
module_i2c_driver ( bmc150_accel_driver ) ;
MODULE_AUTHOR ( " Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " BMC150 I2C accelerometer driver " ) ;
2022-01-16 18:05:31 +00:00
MODULE_IMPORT_NS ( IIO_BMC150 ) ;