268 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * cros_ec_light_prox - Driver for light and prox sensors behing CrosEC.4 *5 * Copyright (C) 2017 Google, Inc6 */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/mod_devicetable.h>18#include <linux/module.h>19#include <linux/platform_data/cros_ec_commands.h>20#include <linux/platform_data/cros_ec_proto.h>21#include <linux/platform_device.h>22#include <linux/slab.h>23 24/*25 * We only represent one entry for light or proximity. EC is merging different26 * light sensors to return the what the eye would see. For proximity, we27 * currently support only one light source.28 */29#define CROS_EC_LIGHT_PROX_MAX_CHANNELS (1 + 1)30 31/* State data for ec_sensors iio driver. */32struct cros_ec_light_prox_state {33 /* Shared by all sensors */34 struct cros_ec_sensors_core_state core;35 36 struct iio_chan_spec channels[CROS_EC_LIGHT_PROX_MAX_CHANNELS];37};38 39static int cros_ec_light_prox_read(struct iio_dev *indio_dev,40 struct iio_chan_spec const *chan,41 int *val, int *val2, long mask)42{43 struct cros_ec_light_prox_state *st = iio_priv(indio_dev);44 u16 data = 0;45 s64 val64;46 int ret;47 int idx = chan->scan_index;48 49 mutex_lock(&st->core.cmd_lock);50 51 switch (mask) {52 case IIO_CHAN_INFO_RAW:53 if (chan->type == IIO_PROXIMITY) {54 ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,55 (s16 *)&data);56 if (ret)57 break;58 *val = data;59 ret = IIO_VAL_INT;60 } else {61 ret = -EINVAL;62 }63 break;64 case IIO_CHAN_INFO_PROCESSED:65 if (chan->type == IIO_LIGHT) {66 ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,67 (s16 *)&data);68 if (ret)69 break;70 /*71 * The data coming from the light sensor is72 * pre-processed and represents the ambient light73 * 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 ret = cros_ec_motion_send_host_cmd(&st->core, 0);86 if (ret)87 break;88 89 /* Save values */90 st->core.calib[0].offset =91 st->core.resp->sensor_offset.offset[0];92 93 *val = st->core.calib[idx].offset;94 ret = IIO_VAL_INT;95 break;96 case IIO_CHAN_INFO_CALIBSCALE:97 /*98 * RANGE is used for calibration99 * scale is a number x.y, where x is coded on 16 bits,100 * y coded on 16 bits, between 0 and 9999.101 */102 st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;103 st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;104 105 ret = cros_ec_motion_send_host_cmd(&st->core, 0);106 if (ret)107 break;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;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].offset = 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] =142 st->core.calib[0].offset;143 st->core.param.sensor_offset.temp =144 EC_MOTION_SENSE_INVALID_CALIB_TEMP;145 ret = cros_ec_motion_send_host_cmd(&st->core, 0);146 break;147 case IIO_CHAN_INFO_CALIBSCALE:148 st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;149 st->core.curr_range = (val << 16) | (val2 / 100);150 st->core.param.sensor_range.data = st->core.curr_range;151 ret = cros_ec_motion_send_host_cmd(&st->core, 0);152 if (ret == 0)153 st->core.range_updated = true;154 break;155 default:156 ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,157 mask);158 break;159 }160 161 mutex_unlock(&st->core.cmd_lock);162 163 return ret;164}165 166static const struct iio_info cros_ec_light_prox_info = {167 .read_raw = &cros_ec_light_prox_read,168 .write_raw = &cros_ec_light_prox_write,169 .read_avail = &cros_ec_sensors_core_read_avail,170};171 172static int cros_ec_light_prox_probe(struct platform_device *pdev)173{174 struct device *dev = &pdev->dev;175 struct iio_dev *indio_dev;176 struct cros_ec_light_prox_state *state;177 struct iio_chan_spec *channel;178 int ret;179 180 indio_dev = devm_iio_device_alloc(dev, sizeof(*state));181 if (!indio_dev)182 return -ENOMEM;183 184 ret = cros_ec_sensors_core_init(pdev, indio_dev, true,185 cros_ec_sensors_capture);186 if (ret)187 return ret;188 189 indio_dev->info = &cros_ec_light_prox_info;190 state = iio_priv(indio_dev);191 channel = state->channels;192 193 /* Common part */194 channel->info_mask_shared_by_all =195 BIT(IIO_CHAN_INFO_SAMP_FREQ);196 channel->info_mask_shared_by_all_available =197 BIT(IIO_CHAN_INFO_SAMP_FREQ);198 channel->scan_type.realbits = CROS_EC_SENSOR_BITS;199 channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;200 channel->scan_type.shift = 0;201 channel->scan_index = 0;202 channel->ext_info = cros_ec_sensors_ext_info;203 channel->scan_type.sign = 'u';204 205 /* Sensor specific */206 switch (state->core.type) {207 case MOTIONSENSE_TYPE_LIGHT:208 channel->type = IIO_LIGHT;209 channel->info_mask_separate =210 BIT(IIO_CHAN_INFO_PROCESSED) |211 BIT(IIO_CHAN_INFO_CALIBBIAS) |212 BIT(IIO_CHAN_INFO_CALIBSCALE);213 break;214 case MOTIONSENSE_TYPE_PROX:215 channel->type = IIO_PROXIMITY;216 channel->info_mask_separate =217 BIT(IIO_CHAN_INFO_RAW) |218 BIT(IIO_CHAN_INFO_CALIBBIAS) |219 BIT(IIO_CHAN_INFO_CALIBSCALE);220 break;221 default:222 dev_warn(dev, "Unknown motion sensor\n");223 return -EINVAL;224 }225 226 /* Timestamp */227 channel++;228 channel->type = IIO_TIMESTAMP;229 channel->channel = -1;230 channel->scan_index = 1;231 channel->scan_type.sign = 's';232 channel->scan_type.realbits = 64;233 channel->scan_type.storagebits = 64;234 235 indio_dev->channels = state->channels;236 237 indio_dev->num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS;238 239 state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;240 241 return cros_ec_sensors_core_register(dev, indio_dev,242 cros_ec_sensors_push_data);243}244 245static const struct platform_device_id cros_ec_light_prox_ids[] = {246 {247 .name = "cros-ec-prox",248 },249 {250 .name = "cros-ec-light",251 },252 { /* sentinel */ }253};254MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);255 256static struct platform_driver cros_ec_light_prox_platform_driver = {257 .driver = {258 .name = "cros-ec-light-prox",259 .pm = &cros_ec_sensors_pm_ops,260 },261 .probe = cros_ec_light_prox_probe,262 .id_table = cros_ec_light_prox_ids,263};264module_platform_driver(cros_ec_light_prox_platform_driver);265 266MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");267MODULE_LICENSE("GPL v2");268