brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 19e529c Raw
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