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/mfd/cros_ec.h>
  18#include <linux/mfd/cros_ec_commands.h>
  19#include <linux/module.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 = IIO_VAL_INT;
  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                        if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
  54                                                     (s16 *)&data) < 0) {
  55                                ret = -EIO;
  56                                break;
  57                        }
  58                        *val = data;
  59                } else {
  60                        ret = -EINVAL;
  61                }
  62                break;
  63        case IIO_CHAN_INFO_PROCESSED:
  64                if (chan->type == IIO_LIGHT) {
  65                        if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
  66                                                     (s16 *)&data) < 0) {
  67                                ret = -EIO;
  68                                break;
  69                        }
  70                        /*
  71                         * The data coming from the light sensor is
  72                         * pre-processed and represents the ambient light
  73                         * illuminance reading expressed in lux.
  74                         */
  75                        *val = data;
  76                        ret = IIO_VAL_INT;
  77                } else {
  78                        ret = -EINVAL;
  79                }
  80                break;
  81        case IIO_CHAN_INFO_CALIBBIAS:
  82                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
  83                st->core.param.sensor_offset.flags = 0;
  84
  85                if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
  86                        ret = -EIO;
  87                        break;
  88                }
  89
  90                /* Save values */
  91                st->core.calib[0] = st->core.resp->sensor_offset.offset[0];
  92
  93                *val = st->core.calib[idx];
  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                if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
 105                        ret = -EIO;
 106                        break;
 107                }
 108
 109                val64 = st->core.resp->sensor_range.ret;
 110                *val = val64 >> 16;
 111                *val2 = (val64 & 0xffff) * 100;
 112                ret = IIO_VAL_INT_PLUS_MICRO;
 113                break;
 114        default:
 115                ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
 116                                                mask);
 117                break;
 118        }
 119
 120        mutex_unlock(&st->core.cmd_lock);
 121
 122        return ret;
 123}
 124
 125static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
 126                               struct iio_chan_spec const *chan,
 127                               int val, int val2, long mask)
 128{
 129        struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
 130        int ret = 0;
 131        int idx = chan->scan_index;
 132
 133        mutex_lock(&st->core.cmd_lock);
 134
 135        switch (mask) {
 136        case IIO_CHAN_INFO_CALIBBIAS:
 137                st->core.calib[idx] = val;
 138                /* Send to EC for each axis, even if not complete */
 139                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
 140                st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET;
 141                st->core.param.sensor_offset.offset[0] = st->core.calib[0];
 142                st->core.param.sensor_offset.temp =
 143                                        EC_MOTION_SENSE_INVALID_CALIB_TEMP;
 144                if (cros_ec_motion_send_host_cmd(&st->core, 0))
 145                        ret = -EIO;
 146                break;
 147        case IIO_CHAN_INFO_CALIBSCALE:
 148                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
 149                st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
 150                if (cros_ec_motion_send_host_cmd(&st->core, 0))
 151                        ret = -EIO;
 152                break;
 153        default:
 154                ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
 155                                                 mask);
 156                break;
 157        }
 158
 159        mutex_unlock(&st->core.cmd_lock);
 160
 161        return ret;
 162}
 163
 164static const struct iio_info cros_ec_light_prox_info = {
 165        .read_raw = &cros_ec_light_prox_read,
 166        .write_raw = &cros_ec_light_prox_write,
 167};
 168
 169static int cros_ec_light_prox_probe(struct platform_device *pdev)
 170{
 171        struct device *dev = &pdev->dev;
 172        struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
 173        struct iio_dev *indio_dev;
 174        struct cros_ec_light_prox_state *state;
 175        struct iio_chan_spec *channel;
 176        int ret;
 177
 178        if (!ec_dev || !ec_dev->ec_dev) {
 179                dev_warn(dev, "No CROS EC device found.\n");
 180                return -EINVAL;
 181        }
 182
 183        indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
 184        if (!indio_dev)
 185                return -ENOMEM;
 186
 187        ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
 188        if (ret)
 189                return ret;
 190
 191        indio_dev->info = &cros_ec_light_prox_info;
 192        state = iio_priv(indio_dev);
 193        state->core.type = state->core.resp->info.type;
 194        state->core.loc = state->core.resp->info.location;
 195        channel = state->channels;
 196
 197        /* Common part */
 198        channel->info_mask_shared_by_all =
 199                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
 200                BIT(IIO_CHAN_INFO_FREQUENCY);
 201        channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
 202        channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
 203        channel->scan_type.shift = 0;
 204        channel->scan_index = 0;
 205        channel->ext_info = cros_ec_sensors_ext_info;
 206        channel->scan_type.sign = 'u';
 207
 208        state->core.calib[0] = 0;
 209
 210        /* Sensor specific */
 211        switch (state->core.type) {
 212        case MOTIONSENSE_TYPE_LIGHT:
 213                channel->type = IIO_LIGHT;
 214                channel->info_mask_separate =
 215                        BIT(IIO_CHAN_INFO_PROCESSED) |
 216                        BIT(IIO_CHAN_INFO_CALIBBIAS) |
 217                        BIT(IIO_CHAN_INFO_CALIBSCALE);
 218                break;
 219        case MOTIONSENSE_TYPE_PROX:
 220                channel->type = IIO_PROXIMITY;
 221                channel->info_mask_separate =
 222                        BIT(IIO_CHAN_INFO_RAW) |
 223                        BIT(IIO_CHAN_INFO_CALIBBIAS) |
 224                        BIT(IIO_CHAN_INFO_CALIBSCALE);
 225                break;
 226        default:
 227                dev_warn(dev, "Unknown motion sensor\n");
 228                return -EINVAL;
 229        }
 230
 231        /* Timestamp */
 232        channel++;
 233        channel->type = IIO_TIMESTAMP;
 234        channel->channel = -1;
 235        channel->scan_index = 1;
 236        channel->scan_type.sign = 's';
 237        channel->scan_type.realbits = 64;
 238        channel->scan_type.storagebits = 64;
 239
 240        indio_dev->channels = state->channels;
 241
 242        indio_dev->num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS;
 243
 244        state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
 245
 246        ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
 247                                              cros_ec_sensors_capture, NULL);
 248        if (ret)
 249                return ret;
 250
 251        return devm_iio_device_register(dev, indio_dev);
 252}
 253
 254static const struct platform_device_id cros_ec_light_prox_ids[] = {
 255        {
 256                .name = "cros-ec-prox",
 257        },
 258        {
 259                .name = "cros-ec-light",
 260        },
 261        { /* sentinel */ }
 262};
 263MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
 264
 265static struct platform_driver cros_ec_light_prox_platform_driver = {
 266        .driver = {
 267                .name   = "cros-ec-light-prox",
 268                .pm     = &cros_ec_sensors_pm_ops,
 269        },
 270        .probe          = cros_ec_light_prox_probe,
 271        .id_table       = cros_ec_light_prox_ids,
 272};
 273module_platform_driver(cros_ec_light_prox_platform_driver);
 274
 275MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");
 276MODULE_LICENSE("GPL v2");
 277