2019-03-13 14:41:20 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-01-30 20:07:53 +03:00
/*
* cros_ec_light_prox - Driver for light and prox sensors behing CrosEC .
*
* Copyright ( C ) 2017 Google , Inc
*/
# include <linux/device.h>
# include <linux/iio/buffer.h>
2018-03-14 00:23:28 +03:00
# include <linux/iio/common/cros_ec_sensors_core.h>
2017-01-30 20:07:53 +03:00
# include <linux/iio/iio.h>
# include <linux/iio/kfifo_buf.h>
# include <linux/iio/trigger.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/kernel.h>
2022-06-10 11:45:27 +03:00
# include <linux/mod_devicetable.h>
2017-01-30 20:07:53 +03:00
# include <linux/module.h>
2019-09-02 12:53:05 +03:00
# include <linux/platform_data/cros_ec_commands.h>
# include <linux/platform_data/cros_ec_proto.h>
2017-01-30 20:07:53 +03:00
# include <linux/platform_device.h>
# include <linux/slab.h>
/*
* We only represent one entry for light or proximity . EC is merging different
* light sensors to return the what the eye would see . For proximity , we
* currently support only one light source .
*/
# define CROS_EC_LIGHT_PROX_MAX_CHANNELS (1 + 1)
/* State data for ec_sensors iio driver. */
struct cros_ec_light_prox_state {
/* Shared by all sensors */
struct cros_ec_sensors_core_state core ;
struct iio_chan_spec channels [ CROS_EC_LIGHT_PROX_MAX_CHANNELS ] ;
} ;
static int cros_ec_light_prox_read ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct cros_ec_light_prox_state * st = iio_priv ( indio_dev ) ;
u16 data = 0 ;
s64 val64 ;
2019-07-19 01:22:37 +03:00
int ret ;
2017-01-30 20:07:53 +03:00
int idx = chan - > scan_index ;
mutex_lock ( & st - > core . cmd_lock ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
if ( chan - > type = = IIO_PROXIMITY ) {
2019-07-19 01:22:37 +03:00
ret = cros_ec_sensors_read_cmd ( indio_dev , 1 < < idx ,
( s16 * ) & data ) ;
if ( ret )
2017-01-30 20:07:53 +03:00
break ;
* val = data ;
2019-07-19 01:22:37 +03:00
ret = IIO_VAL_INT ;
2017-01-30 20:07:53 +03:00
} else {
ret = - EINVAL ;
}
break ;
case IIO_CHAN_INFO_PROCESSED :
if ( chan - > type = = IIO_LIGHT ) {
2019-07-19 01:22:37 +03:00
ret = cros_ec_sensors_read_cmd ( indio_dev , 1 < < idx ,
( s16 * ) & data ) ;
if ( ret )
2017-01-30 20:07:53 +03:00
break ;
/*
* The data coming from the light sensor is
* pre - processed and represents the ambient light
* illuminance reading expressed in lux .
*/
* val = data ;
ret = IIO_VAL_INT ;
} else {
ret = - EINVAL ;
}
break ;
case IIO_CHAN_INFO_CALIBBIAS :
st - > core . param . cmd = MOTIONSENSE_CMD_SENSOR_OFFSET ;
st - > core . param . sensor_offset . flags = 0 ;
2019-07-19 01:22:37 +03:00
ret = cros_ec_motion_send_host_cmd ( & st - > core , 0 ) ;
if ( ret )
2017-01-30 20:07:53 +03:00
break ;
/* Save values */
2019-07-19 02:28:24 +03:00
st - > core . calib [ 0 ] . offset =
st - > core . resp - > sensor_offset . offset [ 0 ] ;
2017-01-30 20:07:53 +03:00
2019-07-19 02:28:24 +03:00
* val = st - > core . calib [ idx ] . offset ;
2019-07-19 01:22:37 +03:00
ret = IIO_VAL_INT ;
2017-01-30 20:07:53 +03:00
break ;
case IIO_CHAN_INFO_CALIBSCALE :
/*
* RANGE is used for calibration
* scale is a number x . y , where x is coded on 16 bits ,
* y coded on 16 bits , between 0 and 9999.
*/
st - > core . param . cmd = MOTIONSENSE_CMD_SENSOR_RANGE ;
st - > core . param . sensor_range . data = EC_MOTION_SENSE_NO_VALUE ;
2019-07-19 01:22:37 +03:00
ret = cros_ec_motion_send_host_cmd ( & st - > core , 0 ) ;
if ( ret )
2017-01-30 20:07:53 +03:00
break ;
val64 = st - > core . resp - > sensor_range . ret ;
* val = val64 > > 16 ;
* val2 = ( val64 & 0xffff ) * 100 ;
ret = IIO_VAL_INT_PLUS_MICRO ;
break ;
default :
ret = cros_ec_sensors_core_read ( & st - > core , chan , val , val2 ,
mask ) ;
break ;
}
mutex_unlock ( & st - > core . cmd_lock ) ;
return ret ;
}
static int cros_ec_light_prox_write ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct cros_ec_light_prox_state * st = iio_priv ( indio_dev ) ;
2019-07-19 01:22:37 +03:00
int ret ;
2017-01-30 20:07:53 +03:00
int idx = chan - > scan_index ;
mutex_lock ( & st - > core . cmd_lock ) ;
switch ( mask ) {
case IIO_CHAN_INFO_CALIBBIAS :
2019-07-19 02:28:24 +03:00
st - > core . calib [ idx ] . offset = val ;
2017-01-30 20:07:53 +03:00
/* Send to EC for each axis, even if not complete */
st - > core . param . cmd = MOTIONSENSE_CMD_SENSOR_OFFSET ;
st - > core . param . sensor_offset . flags = MOTION_SENSE_SET_OFFSET ;
2019-07-19 02:28:24 +03:00
st - > core . param . sensor_offset . offset [ 0 ] =
st - > core . calib [ 0 ] . offset ;
2017-01-30 20:07:53 +03:00
st - > core . param . sensor_offset . temp =
EC_MOTION_SENSE_INVALID_CALIB_TEMP ;
2019-07-19 01:22:37 +03:00
ret = cros_ec_motion_send_host_cmd ( & st - > core , 0 ) ;
2017-01-30 20:07:53 +03:00
break ;
case IIO_CHAN_INFO_CALIBSCALE :
st - > core . param . cmd = MOTIONSENSE_CMD_SENSOR_RANGE ;
2020-05-27 07:35:17 +03:00
st - > core . curr_range = ( val < < 16 ) | ( val2 / 100 ) ;
st - > core . param . sensor_range . data = st - > core . curr_range ;
2019-07-19 01:22:37 +03:00
ret = cros_ec_motion_send_host_cmd ( & st - > core , 0 ) ;
2020-05-27 07:35:17 +03:00
if ( ret = = 0 )
st - > core . range_updated = true ;
2017-01-30 20:07:53 +03:00
break ;
default :
ret = cros_ec_sensors_core_write ( & st - > core , chan , val , val2 ,
mask ) ;
break ;
}
mutex_unlock ( & st - > core . cmd_lock ) ;
return ret ;
}
static const struct iio_info cros_ec_light_prox_info = {
. read_raw = & cros_ec_light_prox_read ,
. write_raw = & cros_ec_light_prox_write ,
2019-07-16 12:11:06 +03:00
. read_avail = & cros_ec_sensors_core_read_avail ,
2017-01-30 20:07:53 +03:00
} ;
static int cros_ec_light_prox_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct iio_dev * indio_dev ;
struct cros_ec_light_prox_state * state ;
struct iio_chan_spec * channel ;
int ret ;
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * state ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
2020-03-28 01:34:38 +03:00
ret = cros_ec_sensors_core_init ( pdev , indio_dev , true ,
2022-07-11 17:47:16 +03:00
cros_ec_sensors_capture ) ;
2017-01-30 20:07:53 +03:00
if ( ret )
return ret ;
indio_dev - > info = & cros_ec_light_prox_info ;
state = iio_priv ( indio_dev ) ;
channel = state - > channels ;
/* Common part */
channel - > info_mask_shared_by_all =
2020-03-28 01:34:40 +03:00
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ;
2019-07-16 12:11:06 +03:00
channel - > info_mask_shared_by_all_available =
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ;
2017-01-30 20:07:53 +03:00
channel - > scan_type . realbits = CROS_EC_SENSOR_BITS ;
channel - > scan_type . storagebits = CROS_EC_SENSOR_BITS ;
channel - > scan_type . shift = 0 ;
channel - > scan_index = 0 ;
channel - > ext_info = cros_ec_sensors_ext_info ;
channel - > scan_type . sign = ' u ' ;
/* Sensor specific */
switch ( state - > core . type ) {
case MOTIONSENSE_TYPE_LIGHT :
channel - > type = IIO_LIGHT ;
channel - > info_mask_separate =
BIT ( IIO_CHAN_INFO_PROCESSED ) |
BIT ( IIO_CHAN_INFO_CALIBBIAS ) |
BIT ( IIO_CHAN_INFO_CALIBSCALE ) ;
break ;
case MOTIONSENSE_TYPE_PROX :
channel - > type = IIO_PROXIMITY ;
channel - > info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_CALIBBIAS ) |
BIT ( IIO_CHAN_INFO_CALIBSCALE ) ;
break ;
default :
dev_warn ( dev , " Unknown motion sensor \n " ) ;
return - EINVAL ;
}
/* Timestamp */
channel + + ;
channel - > type = IIO_TIMESTAMP ;
channel - > channel = - 1 ;
channel - > scan_index = 1 ;
channel - > scan_type . sign = ' s ' ;
channel - > scan_type . realbits = 64 ;
channel - > scan_type . storagebits = 64 ;
indio_dev - > channels = state - > channels ;
indio_dev - > num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS ;
state - > core . read_ec_sensors_data = cros_ec_sensors_read_cmd ;
2022-07-11 17:47:16 +03:00
return cros_ec_sensors_core_register ( dev , indio_dev ,
cros_ec_sensors_push_data ) ;
2017-01-30 20:07:53 +03:00
}
static const struct platform_device_id cros_ec_light_prox_ids [ ] = {
{
. name = " cros-ec-prox " ,
} ,
{
. name = " cros-ec-light " ,
} ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( platform , cros_ec_light_prox_ids ) ;
static struct platform_driver cros_ec_light_prox_platform_driver = {
. driver = {
. name = " cros-ec-light-prox " ,
2020-05-27 07:35:17 +03:00
. pm = & cros_ec_sensors_pm_ops ,
2017-01-30 20:07:53 +03:00
} ,
. probe = cros_ec_light_prox_probe ,
. id_table = cros_ec_light_prox_ids ,
} ;
module_platform_driver ( cros_ec_light_prox_platform_driver ) ;
MODULE_DESCRIPTION ( " ChromeOS EC light/proximity sensors driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;