Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * cros_ec_light_prox - Driver for light and prox sensors behing CrosEC.
0004  *
0005  * Copyright (C) 2017 Google, Inc
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/iio/buffer.h>
0010 #include <linux/iio/common/cros_ec_sensors_core.h>
0011 #include <linux/iio/iio.h>
0012 #include <linux/iio/kfifo_buf.h>
0013 #include <linux/iio/trigger.h>
0014 #include <linux/iio/triggered_buffer.h>
0015 #include <linux/iio/trigger_consumer.h>
0016 #include <linux/kernel.h>
0017 #include <linux/mod_devicetable.h>
0018 #include <linux/module.h>
0019 #include <linux/platform_data/cros_ec_commands.h>
0020 #include <linux/platform_data/cros_ec_proto.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/slab.h>
0023 
0024 /*
0025  * We only represent one entry for light or proximity. EC is merging different
0026  * light sensors to return the what the eye would see. For proximity, we
0027  * currently support only one light source.
0028  */
0029 #define CROS_EC_LIGHT_PROX_MAX_CHANNELS (1 + 1)
0030 
0031 /* State data for ec_sensors iio driver. */
0032 struct cros_ec_light_prox_state {
0033     /* Shared by all sensors */
0034     struct cros_ec_sensors_core_state core;
0035 
0036     struct iio_chan_spec channels[CROS_EC_LIGHT_PROX_MAX_CHANNELS];
0037 };
0038 
0039 static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
0040                    struct iio_chan_spec const *chan,
0041                    int *val, int *val2, long mask)
0042 {
0043     struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
0044     u16 data = 0;
0045     s64 val64;
0046     int ret;
0047     int idx = chan->scan_index;
0048 
0049     mutex_lock(&st->core.cmd_lock);
0050 
0051     switch (mask) {
0052     case IIO_CHAN_INFO_RAW:
0053         if (chan->type == IIO_PROXIMITY) {
0054             ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
0055                              (s16 *)&data);
0056             if (ret)
0057                 break;
0058             *val = data;
0059             ret = IIO_VAL_INT;
0060         } else {
0061             ret = -EINVAL;
0062         }
0063         break;
0064     case IIO_CHAN_INFO_PROCESSED:
0065         if (chan->type == IIO_LIGHT) {
0066             ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
0067                              (s16 *)&data);
0068             if (ret)
0069                 break;
0070             /*
0071              * The data coming from the light sensor is
0072              * pre-processed and represents the ambient light
0073              * illuminance reading expressed in lux.
0074              */
0075             *val = data;
0076             ret = IIO_VAL_INT;
0077         } else {
0078             ret = -EINVAL;
0079         }
0080         break;
0081     case IIO_CHAN_INFO_CALIBBIAS:
0082         st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
0083         st->core.param.sensor_offset.flags = 0;
0084 
0085         ret = cros_ec_motion_send_host_cmd(&st->core, 0);
0086         if (ret)
0087             break;
0088 
0089         /* Save values */
0090         st->core.calib[0].offset =
0091             st->core.resp->sensor_offset.offset[0];
0092 
0093         *val = st->core.calib[idx].offset;
0094         ret = IIO_VAL_INT;
0095         break;
0096     case IIO_CHAN_INFO_CALIBSCALE:
0097         /*
0098          * RANGE is used for calibration
0099          * scale is a number x.y, where x is coded on 16 bits,
0100          * y coded on 16 bits, between 0 and 9999.
0101          */
0102         st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
0103         st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
0104 
0105         ret = cros_ec_motion_send_host_cmd(&st->core, 0);
0106         if (ret)
0107             break;
0108 
0109         val64 = st->core.resp->sensor_range.ret;
0110         *val = val64 >> 16;
0111         *val2 = (val64 & 0xffff) * 100;
0112         ret = IIO_VAL_INT_PLUS_MICRO;
0113         break;
0114     default:
0115         ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
0116                         mask);
0117         break;
0118     }
0119 
0120     mutex_unlock(&st->core.cmd_lock);
0121 
0122     return ret;
0123 }
0124 
0125 static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
0126                    struct iio_chan_spec const *chan,
0127                    int val, int val2, long mask)
0128 {
0129     struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
0130     int ret;
0131     int idx = chan->scan_index;
0132 
0133     mutex_lock(&st->core.cmd_lock);
0134 
0135     switch (mask) {
0136     case IIO_CHAN_INFO_CALIBBIAS:
0137         st->core.calib[idx].offset = val;
0138         /* Send to EC for each axis, even if not complete */
0139         st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
0140         st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET;
0141         st->core.param.sensor_offset.offset[0] =
0142             st->core.calib[0].offset;
0143         st->core.param.sensor_offset.temp =
0144                     EC_MOTION_SENSE_INVALID_CALIB_TEMP;
0145         ret = cros_ec_motion_send_host_cmd(&st->core, 0);
0146         break;
0147     case IIO_CHAN_INFO_CALIBSCALE:
0148         st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
0149         st->core.curr_range = (val << 16) | (val2 / 100);
0150         st->core.param.sensor_range.data = st->core.curr_range;
0151         ret = cros_ec_motion_send_host_cmd(&st->core, 0);
0152         if (ret == 0)
0153             st->core.range_updated = true;
0154         break;
0155     default:
0156         ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
0157                          mask);
0158         break;
0159     }
0160 
0161     mutex_unlock(&st->core.cmd_lock);
0162 
0163     return ret;
0164 }
0165 
0166 static const struct iio_info cros_ec_light_prox_info = {
0167     .read_raw = &cros_ec_light_prox_read,
0168     .write_raw = &cros_ec_light_prox_write,
0169     .read_avail = &cros_ec_sensors_core_read_avail,
0170 };
0171 
0172 static int cros_ec_light_prox_probe(struct platform_device *pdev)
0173 {
0174     struct device *dev = &pdev->dev;
0175     struct iio_dev *indio_dev;
0176     struct cros_ec_light_prox_state *state;
0177     struct iio_chan_spec *channel;
0178     int ret;
0179 
0180     indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
0181     if (!indio_dev)
0182         return -ENOMEM;
0183 
0184     ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
0185                     cros_ec_sensors_capture);
0186     if (ret)
0187         return ret;
0188 
0189     indio_dev->info = &cros_ec_light_prox_info;
0190     state = iio_priv(indio_dev);
0191     channel = state->channels;
0192 
0193     /* Common part */
0194     channel->info_mask_shared_by_all =
0195         BIT(IIO_CHAN_INFO_SAMP_FREQ);
0196     channel->info_mask_shared_by_all_available =
0197         BIT(IIO_CHAN_INFO_SAMP_FREQ);
0198     channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
0199     channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
0200     channel->scan_type.shift = 0;
0201     channel->scan_index = 0;
0202     channel->ext_info = cros_ec_sensors_ext_info;
0203     channel->scan_type.sign = 'u';
0204 
0205     /* Sensor specific */
0206     switch (state->core.type) {
0207     case MOTIONSENSE_TYPE_LIGHT:
0208         channel->type = IIO_LIGHT;
0209         channel->info_mask_separate =
0210             BIT(IIO_CHAN_INFO_PROCESSED) |
0211             BIT(IIO_CHAN_INFO_CALIBBIAS) |
0212             BIT(IIO_CHAN_INFO_CALIBSCALE);
0213         break;
0214     case MOTIONSENSE_TYPE_PROX:
0215         channel->type = IIO_PROXIMITY;
0216         channel->info_mask_separate =
0217             BIT(IIO_CHAN_INFO_RAW) |
0218             BIT(IIO_CHAN_INFO_CALIBBIAS) |
0219             BIT(IIO_CHAN_INFO_CALIBSCALE);
0220         break;
0221     default:
0222         dev_warn(dev, "Unknown motion sensor\n");
0223         return -EINVAL;
0224     }
0225 
0226     /* Timestamp */
0227     channel++;
0228     channel->type = IIO_TIMESTAMP;
0229     channel->channel = -1;
0230     channel->scan_index = 1;
0231     channel->scan_type.sign = 's';
0232     channel->scan_type.realbits = 64;
0233     channel->scan_type.storagebits = 64;
0234 
0235     indio_dev->channels = state->channels;
0236 
0237     indio_dev->num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS;
0238 
0239     state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
0240 
0241     return cros_ec_sensors_core_register(dev, indio_dev,
0242                          cros_ec_sensors_push_data);
0243 }
0244 
0245 static const struct platform_device_id cros_ec_light_prox_ids[] = {
0246     {
0247         .name = "cros-ec-prox",
0248     },
0249     {
0250         .name = "cros-ec-light",
0251     },
0252     { /* sentinel */ }
0253 };
0254 MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
0255 
0256 static struct platform_driver cros_ec_light_prox_platform_driver = {
0257     .driver = {
0258         .name   = "cros-ec-light-prox",
0259         .pm = &cros_ec_sensors_pm_ops,
0260     },
0261     .probe      = cros_ec_light_prox_probe,
0262     .id_table   = cros_ec_light_prox_ids,
0263 };
0264 module_platform_driver(cros_ec_light_prox_platform_driver);
0265 
0266 MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");
0267 MODULE_LICENSE("GPL v2");