linux/drivers/iio/light/cros_ec_light_prox.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * cros_ec_light_prox - Driver for light and prox sensors behing CrosEC.
   4 *
   5 * Copyright (C) 2017 Google, Inc
   6 */
   7
   8#include <linux/device.h>
   9#include <linux/iio/buffer.h>
  10#include <linux/iio/common/cros_ec_sensors_core.h>
  11#include <linux/iio/iio.h>
  12#include <linux/iio/kfifo_buf.h>
  13#include <linux/iio/trigger.h>
  14#include <linux/iio/triggered_buffer.h>
  15#include <linux/iio/trigger_consumer.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/platform_data/cros_ec_commands.h>
  19#include <linux/platform_data/cros_ec_proto.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22
  23/*
  24 * We only represent one entry for light or proximity. EC is merging different
  25 * light sensors to return the what the eye would see. For proximity, we
  26 * currently support only one light source.
  27 */
  28#define CROS_EC_LIGHT_PROX_MAX_CHANNELS (1 + 1)
  29
  30/* State data for ec_sensors iio driver. */
  31struct cros_ec_light_prox_state {
  32        /* Shared by all sensors */
  33        struct cros_ec_sensors_core_state core;
  34
  35        struct iio_chan_spec channels[CROS_EC_LIGHT_PROX_MAX_CHANNELS];
  36};
  37
  38static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
  39                                   struct iio_chan_spec const *chan,
  40                                   int *val, int *val2, long mask)
  41{
  42        struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
  43        u16 data = 0;
  44        s64 val64;
  45        int ret;
  46        int idx = chan->scan_index;
  47
  48        mutex_lock(&st->core.cmd_lock);
  49
  50        switch (mask) {
  51        case IIO_CHAN_INFO_RAW:
  52                if (chan->type == IIO_PROXIMITY) {
  53                        ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
  54                                                     (s16 *)&data);
  55                        if (ret)
  56                                break;
  57                        *val = data;
  58                        ret = IIO_VAL_INT;
  59                } else {
  60                        ret = -EINVAL;
  61                }
  62                break;
  63        case IIO_CHAN_INFO_PROCESSED:
  64                if (chan->type == IIO_LIGHT) {
  65                        ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
  66                                                     (s16 *)&data);
  67                        if (ret)
  68                                break;
  69                        /*
  70                         * The data coming from the light sensor is
  71                         * pre-processed and represents the ambient light
  72                         * illuminance reading expressed in lux.
  73                         */
  74                        *val = data;
  75                        ret = IIO_VAL_INT;
  76                } else {
  77                        ret = -EINVAL;
  78                }
  79                break;
  80        case IIO_CHAN_INFO_CALIBBIAS:
  81                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
  82                st->core.param.sensor_offset.flags = 0;
  83
  84                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
  85                if (ret)
  86                        break;
  87
  88                /* Save values */
  89                st->core.calib[0].offset =
  90                        st->core.resp->sensor_offset.offset[0];
  91
  92                *val = st->core.calib[idx].offset;
  93                ret = IIO_VAL_INT;
  94                break;
  95        case IIO_CHAN_INFO_CALIBSCALE:
  96                /*
  97                 * RANGE is used for calibration
  98                 * scale is a number x.y, where x is coded on 16 bits,
  99                 * y coded on 16 bits, between 0 and 9999.
 100                 */
 101                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
 102                st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
 103
 104                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
 105                if (ret)
 106                        break;
 107
 108                val64 = st->core.resp->sensor_range.ret;
 109                *val = val64 >> 16;
 110                *val2 = (val64 & 0xffff) * 100;
 111                ret = IIO_VAL_INT_PLUS_MICRO;
 112                break;
 113        default:
 114                ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
 115                                                mask);
 116                break;
 117        }
 118
 119        mutex_unlock(&st->core.cmd_lock);
 120
 121        return ret;
 122}
 123
 124static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
 125                               struct iio_chan_spec const *chan,
 126                               int val, int val2, long mask)
 127{
 128        struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
 129        int ret;
 130        int idx = chan->scan_index;
 131
 132        mutex_lock(&st->core.cmd_lock);
 133
 134        switch (mask) {
 135        case IIO_CHAN_INFO_CALIBBIAS:
 136                st->core.calib[idx].offset = val;
 137                /* Send to EC for each axis, even if not complete */
 138                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
 139                st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET;
 140                st->core.param.sensor_offset.offset[0] =
 141                        st->core.calib[0].offset;
 142                st->core.param.sensor_offset.temp =
 143                                        EC_MOTION_SENSE_INVALID_CALIB_TEMP;
 144                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
 145                break;
 146        case IIO_CHAN_INFO_CALIBSCALE:
 147                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
 148                st->core.curr_range = (val << 16) | (val2 / 100);
 149                st->core.param.sensor_range.data = st->core.curr_range;
 150                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
 151                if (ret == 0)
 152                        st->core.range_updated = true;
 153                break;
 154        default:
 155                ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
 156                                                 mask);
 157                break;
 158        }
 159
 160        mutex_unlock(&st->core.cmd_lock);
 161
 162        return ret;
 163}
 164
 165static const struct iio_info cros_ec_light_prox_info = {
 166        .read_raw = &cros_ec_light_prox_read,
 167        .write_raw = &cros_ec_light_prox_write,
 168        .read_avail = &cros_ec_sensors_core_read_avail,
 169};
 170
 171static int cros_ec_light_prox_probe(struct platform_device *pdev)
 172{
 173        struct device *dev = &pdev->dev;
 174        struct iio_dev *indio_dev;
 175        struct cros_ec_light_prox_state *state;
 176        struct iio_chan_spec *channel;
 177        int ret;
 178
 179        indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
 180        if (!indio_dev)
 181                return -ENOMEM;
 182
 183        ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
 184                                        cros_ec_sensors_capture,
 185                                        cros_ec_sensors_push_data);
 186        if (ret)
 187                return ret;
 188
 189        indio_dev->info = &cros_ec_light_prox_info;
 190        state = iio_priv(indio_dev);
 191        state->core.type = state->core.resp->info.type;
 192        state->core.loc = state->core.resp->info.location;
 193        channel = state->channels;
 194
 195        /* Common part */
 196        channel->info_mask_shared_by_all =
 197                BIT(IIO_CHAN_INFO_SAMP_FREQ);
 198        channel->info_mask_shared_by_all_available =
 199                BIT(IIO_CHAN_INFO_SAMP_FREQ);
 200        channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
 201        channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
 202        channel->scan_type.shift = 0;
 203        channel->scan_index = 0;
 204        channel->ext_info = cros_ec_sensors_ext_info;
 205        channel->scan_type.sign = 'u';
 206
 207        /* Sensor specific */
 208        switch (state->core.type) {
 209        case MOTIONSENSE_TYPE_LIGHT:
 210                channel->type = IIO_LIGHT;
 211                channel->info_mask_separate =
 212                        BIT(IIO_CHAN_INFO_PROCESSED) |
 213                        BIT(IIO_CHAN_INFO_CALIBBIAS) |
 214                        BIT(IIO_CHAN_INFO_CALIBSCALE);
 215                break;
 216        case MOTIONSENSE_TYPE_PROX:
 217                channel->type = IIO_PROXIMITY;
 218                channel->info_mask_separate =
 219                        BIT(IIO_CHAN_INFO_RAW) |
 220                        BIT(IIO_CHAN_INFO_CALIBBIAS) |
 221                        BIT(IIO_CHAN_INFO_CALIBSCALE);
 222                break;
 223        default:
 224                dev_warn(dev, "Unknown motion sensor\n");
 225                return -EINVAL;
 226        }
 227
 228        /* Timestamp */
 229        channel++;
 230        channel->type = IIO_TIMESTAMP;
 231        channel->channel = -1;
 232        channel->scan_index = 1;
 233        channel->scan_type.sign = 's';
 234        channel->scan_type.realbits = 64;
 235        channel->scan_type.storagebits = 64;
 236
 237        indio_dev->channels = state->channels;
 238
 239        indio_dev->num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS;
 240
 241        state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
 242
 243        return devm_iio_device_register(dev, indio_dev);
 244}
 245
 246static const struct platform_device_id cros_ec_light_prox_ids[] = {
 247        {
 248                .name = "cros-ec-prox",
 249        },
 250        {
 251                .name = "cros-ec-light",
 252        },
 253        { /* sentinel */ }
 254};
 255MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
 256
 257static struct platform_driver cros_ec_light_prox_platform_driver = {
 258        .driver = {
 259                .name   = "cros-ec-light-prox",
 260                .pm     = &cros_ec_sensors_pm_ops,
 261        },
 262        .probe          = cros_ec_light_prox_probe,
 263        .id_table       = cros_ec_light_prox_ids,
 264};
 265module_platform_driver(cros_ec_light_prox_platform_driver);
 266
 267MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");
 268MODULE_LICENSE("GPL v2");
 269