On 25/07/16 09:38, Enric Balletbo Serra wrote: > Hi Jonathan, > > Many thanks for the review, some answers below. > > 2016-07-24 15:51 GMT+02:00 Jonathan Cameron <jic23@xxxxxxxxxx>: >> On 20/07/16 10:22, Enric Balletbo i Serra wrote: >>> Add the core functions to be able to support the sensors attached behind >>> the ChromeOS Embedded Controller and used by other IIO cros-ec sensor >>> drivers. >>> >>> The cros_ec_sensor_core driver matches with current driver in ChromeOS >>> 4.4 tree, so it includes all the fixes at the moment. The support for >>> this driver was made by Gwendal Grignou. The original patch and all the >>> fixes has been squashed and rebased on top of mainline. >>> >>> Signed-off-by: Gwendal Grignou <gwendal@xxxxxxxxxxxx> >>> Signed-off-by: Guenter Roeck <groeck@xxxxxxxxxxxx> >>> [eballetbo: split, squash and rebase on top of mainline the patches >>> found in ChromeOS tree] >>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@xxxxxxxxxxxxx> >> Few minor bits inline. I'm happy enough with the ABI (except that I would >> like a justification for why userspace needs the id one) >> We should see if anyone else has comments on that. >> >> The location one may well become more general in future (to cover other >> devices). >> >> Jonathan >>> --- >>> >>> Changes since v1: >>> - Check kernel-doc documentation. >>> - Bring this in as an when you need it in the rest of the series. >>> - Fix some spelling mistakes. >>> - Include ABI documentation. >>> - Be more careful with buffer sizes (sprintf -> snprintf) >>> - Add cros_ec_sensors prefix to all function. >>> - Check return values on some functions. >>> >>> Documentation/ABI/testing/sysfs-bus-iio-cros-ec | 25 + >>> drivers/iio/common/Kconfig | 1 + >>> drivers/iio/common/Makefile | 1 + >>> drivers/iio/common/cros_ec_sensors/Kconfig | 14 + >>> drivers/iio/common/cros_ec_sensors/Makefile | 5 + >>> .../common/cros_ec_sensors/cros_ec_sensors_core.c | 505 +++++++++++++++++++++ >>> .../common/cros_ec_sensors/cros_ec_sensors_core.h | 177 ++++++++ >>> include/linux/mfd/cros_ec.h | 9 + >>> include/linux/mfd/cros_ec_commands.h | 99 +++- >>> 9 files changed, 831 insertions(+), 5 deletions(-) >>> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-cros-ec >>> create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig >>> create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile >>> create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c >>> create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h >>> >>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec >>> new file mode 100644 >>> index 0000000..3e626b6 >>> --- /dev/null >>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec >>> @@ -0,0 +1,25 @@ >>> +What: /sys/bus/iio/devices/iio:deviceX/calibrate >>> +Date: July 2015 >>> +KernelVersion: 4.7 >>> +Contact: linux-iio@xxxxxxxxxxxxxxx >>> +Description: >>> + Writing '1' will perform a FOC (Fast Online Calibration). The >>> + corresponding calibration offsets can be read from *_calibbias >>> + entries. >> This one is fine as far as I'm concerned. >>> + >>> +What: /sys/bus/iio/devices/iio:deviceX/id >>> +Date: July 2015 >>> +KernelVersion: 4.7 >>> +Contact: linux-iio@xxxxxxxxxxxxxxx >>> +Description: >>> + The id attribute holds an unique number for the motion sensor >>> + identification, as reported by the ChromeOS Embedded Controller. >> Mostly with these, we've just spat the out to the kernel logs as dev_info >> during startup. >> >> Any reason this needs to be more explictly exposed to userspace? > > After think a bit more about this, and to be honest, I'm not sure if > this is really useful to be exposed to userspace. The only use case > that I'm thinking is that some userspace app uses it to uniquely > identify the sensors that hang from the ChromeOS Embedded Controller, > but I'm not sure about this exist. I'll investigate a bit more for if > I find a good justification, otherwise I'll remove this. > > Maybe someone from chromium can give us more opinions? > >>> + >>> +What: /sys/bus/iio/devices/iio:deviceX/location >>> +Date: July 2015 >>> +KernelVersion: 4.7 >>> +Contact: linux-iio@xxxxxxxxxxxxxxx >>> +Description: >>> + This attribute returns a string with the physical location where >>> + the motion sensor is placed. For example, in a laptop a motion >>> + sensor can be located on the base or on the lid. >> Interesting... >> >> We'll probably want to ultimately use this one more generally. This probably >> won't be the last sensor where we have some means of finding this out! >> > Ok, I will rewrite the sentence to be more generic Actually I wasn't implying there was anything that needed changing in this description, more that we might want to 'promote' it to sysfs-bus-iio at somepoint in the near future (or maybe right now). > >> Please include a list of possible values in the description. > > For now lid and base are the values used, is that ok? > Absolutely. As more options turn up we can expand the valid list. >>> \ No newline at end of file >> Fix that ;) > > Yep, sorry my bad. > >>> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig >>> index 26a6026..e108996 100644 >>> --- a/drivers/iio/common/Kconfig >>> +++ b/drivers/iio/common/Kconfig >>> @@ -2,6 +2,7 @@ >>> # IIO common modules >>> # >>> >>> +source "drivers/iio/common/cros_ec_sensors/Kconfig" >>> source "drivers/iio/common/hid-sensors/Kconfig" >>> source "drivers/iio/common/ms_sensors/Kconfig" >>> source "drivers/iio/common/ssp_sensors/Kconfig" >>> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile >>> index 585da6a..6fa760e 100644 >>> --- a/drivers/iio/common/Makefile >>> +++ b/drivers/iio/common/Makefile >>> @@ -7,6 +7,7 @@ >>> # >>> >>> # When adding new entries keep the list in alphabetical order >>> +obj-y += cros_ec_sensors/ >>> obj-y += hid-sensors/ >>> obj-y += ms_sensors/ >>> obj-y += ssp_sensors/ >>> diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig >>> new file mode 100644 >>> index 0000000..24743be >>> --- /dev/null >>> +++ b/drivers/iio/common/cros_ec_sensors/Kconfig >>> @@ -0,0 +1,14 @@ >>> +# >>> +# Chrome OS Embedded Controller managed sensors library >>> +# >>> +config IIO_CROS_EC_SENSORS_CORE >>> + tristate "ChromeOS EC Sensors Core" >>> + depends on SYSFS && MFD_CROS_EC >>> + select IIO_BUFFER >>> + select IIO_TRIGGERED_BUFFER >>> + help >>> + Base module for the ChromeOS EC Sensors module. >>> + Contains core functions used by other IIO CrosEC sensor >>> + drivers. >>> + Define common attributes and sysfs interrupt handler. >>> + >>> diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile >>> new file mode 100644 >>> index 0000000..95b6901 >>> --- /dev/null >>> +++ b/drivers/iio/common/cros_ec_sensors/Makefile >>> @@ -0,0 +1,5 @@ >>> +# >>> +# Makefile for sensors seen through the ChromeOS EC sensor hub. >>> +# >>> + >>> +obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o >>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c >>> new file mode 100644 >>> index 0000000..70e3c17 >>> --- /dev/null >>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c >>> @@ -0,0 +1,505 @@ >>> +/* >>> + * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver. >>> + * >>> + * Copyright (C) 2016 Google, Inc >>> + * >>> + * This software is licensed under the terms of the GNU General Public >>> + * License version 2, as published by the Free Software Foundation, and >>> + * may be copied, distributed, and modified under those terms. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + */ >>> + >>> +#include <linux/delay.h> >>> +#include <linux/device.h> >>> +#include <linux/iio/buffer.h> >>> +#include <linux/iio/iio.h> >>> +#include <linux/iio/kfifo_buf.h> >>> +#include <linux/iio/trigger_consumer.h> >>> +#include <linux/kernel.h> >>> +#include <linux/mfd/cros_ec.h> >>> +#include <linux/mfd/cros_ec_commands.h> >>> +#include <linux/module.h> >>> +#include <linux/slab.h> >>> +#include <linux/sysfs.h> >>> +#include <linux/platform_device.h> >>> + >>> +#include "cros_ec_sensors_core.h" >>> + >>> +static char *cros_ec_loc[] = { >>> + [MOTIONSENSE_LOC_BASE] = "base", >>> + [MOTIONSENSE_LOC_LID] = "lid", >>> + [MOTIONSENSE_LOC_MAX] = "unknown", >>> +}; >>> + >>> +int cros_ec_sensors_core_init(struct platform_device *pdev, >>> + struct iio_dev *indio_dev, >>> + bool physical_device) >>> +{ >>> + struct device *dev = &pdev->dev; >>> + struct cros_ec_sensors_core_state *state = iio_priv(indio_dev); >>> + struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent); >>> + struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev); >>> + >>> + platform_set_drvdata(pdev, indio_dev); >>> + >>> + state->ec = ec->ec_dev; >>> + state->msg = devm_kzalloc(&pdev->dev, >>> + max((u16)sizeof(struct ec_params_motion_sense), >>> + state->ec->max_response), GFP_KERNEL); >>> + if (!state->msg) >>> + return -ENOMEM; >>> + >>> + state->resp = (struct ec_response_motion_sense *)state->msg->data; >>> + >>> + mutex_init(&state->cmd_lock); >>> + >>> + /* Set up the host command structure. */ >>> + state->msg->version = 2; >>> + state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; >>> + state->msg->outsize = sizeof(struct ec_params_motion_sense); >>> + >>> + indio_dev->dev.parent = &pdev->dev; >>> + indio_dev->name = pdev->name; >>> + >>> + if (physical_device) { >>> + indio_dev->modes = INDIO_DIRECT_MODE; >>> + >>> + state->param.cmd = MOTIONSENSE_CMD_INFO; >>> + state->param.info.sensor_num = sensor_platform->sensor_num; >>> + if (cros_ec_motion_send_host_cmd(state, 0)) { >>> + dev_warn(dev, "Can not access sensor info\n"); >>> + return -EIO; >>> + } >>> + state->type = state->resp->info.type; >>> + state->loc = state->resp->info.location; >>> + } >>> + >>> + return 0; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init); >>> + >>> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state, >>> + u16 opt_length) >>> +{ >>> + int ret; >>> + >>> + if (opt_length) >>> + state->msg->insize = min(opt_length, state->ec->max_response); >>> + else >>> + state->msg->insize = state->ec->max_response; >>> + >>> + memcpy(state->msg->data, &state->param, sizeof(state->param)); >>> + >>> + ret = cros_ec_cmd_xfer_status(state->ec, state->msg); >>> + if (ret < 0) >>> + return -EIO; >>> + >>> + if (ret && >>> + state->resp != (struct ec_response_motion_sense *)state->msg->data) >>> + memcpy(state->resp, state->msg->data, ret); >>> + >>> + return 0; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd); >>> + >>> +static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev, >>> + uintptr_t private, const struct iio_chan_spec *chan, >>> + const char *buf, size_t len) >>> +{ >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + int ret, i; >>> + bool calibrate; >>> + >>> + ret = strtobool(buf, &calibrate); >>> + if (ret < 0) >>> + return ret; >>> + if (!calibrate) >>> + return -EINVAL; >>> + >>> + mutex_lock(&st->cmd_lock); >>> + st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB; >>> + ret = cros_ec_motion_send_host_cmd(st, 0); >>> + if (ret != 0) { >>> + dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n"); >>> + } else { >>> + /* Save values */ >>> + for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++) >>> + st->calib[i].offset = st->resp->perform_calib.offset[i]; >>> + } >>> + mutex_unlock(&st->cmd_lock); >>> + >>> + return ret ? ret : len; >>> +} >>> + >>> +static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev, >>> + uintptr_t private, const struct iio_chan_spec *chan, >>> + char *buf) >>> +{ >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + >>> + return snprintf(buf, PAGE_SIZE, "%d\n", st->param.info.sensor_num); >>> +} >>> + >>> +static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev, >>> + uintptr_t private, const struct iio_chan_spec *chan, >>> + char *buf) >>> +{ >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + >>> + return snprintf(buf, PAGE_SIZE, "%s\n", cros_ec_loc[st->loc]); >>> +} >>> + >>> +const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = { >>> + { >>> + .name = "calibrate", >>> + .shared = IIO_SHARED_BY_ALL, >>> + .write = cros_ec_sensors_calibrate >>> + }, >>> + { >>> + .name = "id", >>> + .shared = IIO_SHARED_BY_ALL, >>> + .read = cros_ec_sensors_id >>> + }, >>> + { >>> + .name = "location", >>> + .shared = IIO_SHARED_BY_ALL, >>> + .read = cros_ec_sensors_loc >>> + }, >>> + { }, >>> +}; >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info); >>> + >>> +/** >>> + * cros_ec_sensors_idx_to_reg - convert index into offset in shared memory >>> + * @st: pointer to state information for device >>> + * @idx: sensor index (should be element of enum sensor_index) >>> + * >>> + * Return: address to read at >>> + */ >>> +static unsigned int cros_ec_sensors_idx_to_reg( >>> + struct cros_ec_sensors_core_state *st, >>> + unsigned int idx) >>> +{ >>> + /* >>> + * When using LPC interface, only space for 2 Accel and one Gyro. >>> + * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle. >>> + */ >>> + if (st->type == MOTIONSENSE_TYPE_ACCEL) >>> + return EC_MEMMAP_ACC_DATA + sizeof(u16) * >>> + (1 + idx + st->param.info.sensor_num * >>> + CROS_EC_SENSOR_MAX_AXIS); >>> + else >>> + return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx; >>> +} >>> + >>> +static int cros_ec_sensors_cmd_read_u8(struct cros_ec_device *ec, >>> + unsigned int offset, u8 *dest) >>> +{ >>> + return ec->cmd_readmem(ec, offset, 1, dest); >>> +} >>> + >>> +static int cros_ec_sensors_cmd_read_u16(struct cros_ec_device *ec, >>> + unsigned int offset, u16 *dest) >>> +{ >>> + __le16 tmp; >>> + int ret = ec->cmd_readmem(ec, offset, 2, &tmp); >>> + >>> + *dest = le16_to_cpu(tmp); >>> + >>> + return ret; >>> +} >>> + >>> +/** >>> + * cros_ec_sensors_read_until_not_busy() - read until is not busy >>> + * >>> + * @st: pointer to state information for device >>> + * >>> + * Read from EC status byte until it reads not busy. >>> + * Return: 8-bit status if ok, -errno on failure. >>> + */ >>> +static int cros_ec_sensors_read_until_not_busy( >>> + struct cros_ec_sensors_core_state *st) >>> +{ >>> + struct cros_ec_device *ec = st->ec; >>> + u8 status; >>> + int ret, attempts = 0; >>> + >>> + ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status); >>> + if (ret < 0) >>> + return ret; >>> + >>> + while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) { >>> + /* Give up after enough attempts, return error. */ >>> + if (attempts++ >= 50) >>> + return -EIO; >>> + >>> + /* Small delay every so often. */ >>> + if (attempts % 5 == 0) >>> + msleep(25); >>> + >>> + ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, >>> + &status); >>> + if (ret < 0) >>> + return ret; >>> + } >>> + >>> + return status; >>> +} >>> + >>> +/** >>> + * read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory >>> + * @indio_dev: pointer to IIO device >>> + * @scan_mask: bitmap of the sensor indices to scan >>> + * @data: location to store data >>> + * >>> + * This is the unsafe function for reading the EC data. It does not guarantee >>> + * that the EC will not modify the data as it is being read in. >>> + * >>> + * Return: 0 on success, -errno on failure. >>> + */ >>> +static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev, >>> + unsigned long scan_mask, s16 *data) >>> +{ >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + struct cros_ec_device *ec = st->ec; >>> + unsigned int i; >>> + int ret; >>> + >>> + /* Read all sensors enabled in scan_mask. Each value is 2 bytes. */ >>> + for_each_set_bit(i, &scan_mask, indio_dev->masklength) { >>> + ret = cros_ec_sensors_cmd_read_u16(ec, >>> + cros_ec_sensors_idx_to_reg(st, i), >>> + data); >>> + if (ret < 0) >>> + return ret; >>> + >>> + data++; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, >>> + unsigned long scan_mask, s16 *data) >>> +{ >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + struct cros_ec_device *ec = st->ec; >>> + u8 samp_id = 0xff, status = 0; >>> + int ret, attempts = 0; >>> + >>> + /* >>> + * Continually read all data from EC until the status byte after >>> + * all reads reflects that the EC is not busy and the sample id >>> + * matches the sample id from before all reads. This guarantees >>> + * that data read in was not modified by the EC while reading. >>> + */ >>> + while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT | >>> + EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) { >>> + /* If we have tried to read too many times, return error. */ >>> + if (attempts++ >= 5) >>> + return -EIO; >>> + >>> + /* Read status byte until EC is not busy. */ >>> + status = cros_ec_sensors_read_until_not_busy(st); >>> + if (status < 0) >>> + return status; >>> + >>> + /* >>> + * Store the current sample id so that we can compare to the >>> + * sample id after reading the data. >>> + */ >>> + samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; >>> + >>> + /* Read all EC data, format it, and store it into data. */ >>> + ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask, >>> + data); >>> + if (ret < 0) >>> + return ret; >>> + >>> + /* Read status byte. */ >>> + ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, >>> + &status); >>> + if (ret < 0) >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc); >>> + >>> +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, >>> + unsigned long scan_mask, s16 *data) >>> +{ >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + int ret; >>> + unsigned int i = 0; >>> + >>> + /* Read all sensor data through a command. */ >>> + st->param.cmd = MOTIONSENSE_CMD_DATA; >>> + ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data)); >>> + if (ret != 0) { >>> + dev_warn(&indio_dev->dev, "Unable to read sensor data\n"); >>> + return ret; >>> + } >>> + >>> + for_each_set_bit(i, &scan_mask, indio_dev->masklength) { >>> + *data = st->resp->data.data[i]; >>> + data++; >>> + } >>> + >>> + return 0; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd); >>> + >>> +irqreturn_t cros_ec_sensors_capture(int irq, void *p) >>> +{ >>> + struct iio_poll_func *pf = p; >>> + struct iio_dev *indio_dev = pf->indio_dev; >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + int ret; >>> + >>> + mutex_lock(&st->cmd_lock); >>> + >>> + /* Clear capture data. */ >>> + memset(st->samples, 0, indio_dev->scan_bytes); >>> + >>> + /* Read data based on which channels are enabled in scan mask. */ >>> + ret = st->read_ec_sensors_data(indio_dev, >>> + *(indio_dev->active_scan_mask), >>> + (s16 *)st->samples); >>> + if (ret < 0) >>> + goto done; >>> + >>> + iio_push_to_buffers_with_timestamp(indio_dev, st->samples, >>> + iio_get_time_ns()); >> The iio_get_time_ns now takes an indio_dev (as we support clock type >> selection now). > > Just found it, fixed in next v3. > >>> + >>> +done: >>> + /* >>> + * Tell the core we are done with this trigger and ready for the >>> + * next one. >>> + */ >>> + iio_trigger_notify_done(indio_dev->trig); >>> + >>> + mutex_unlock(&st->cmd_lock); >>> + >>> + return IRQ_HANDLED; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_capture); >>> + >>> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, >>> + struct iio_chan_spec const *chan, >>> + int *val, int *val2, long mask) >>> +{ >>> + int ret = IIO_VAL_INT; >>> + >>> + switch (mask) { >>> + case IIO_CHAN_INFO_SAMP_FREQ: >>> + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; >>> + st->param.ec_rate.data = >>> + EC_MOTION_SENSE_NO_VALUE; >>> + >>> + if (cros_ec_motion_send_host_cmd(st, 0)) >>> + ret = -EIO; >>> + else >>> + *val = st->resp->ec_rate.ret; >>> + break; >>> + case IIO_CHAN_INFO_FREQUENCY: >>> + st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR; >>> + st->param.sensor_odr.data = >>> + EC_MOTION_SENSE_NO_VALUE; >>> + >>> + if (cros_ec_motion_send_host_cmd(st, 0)) >>> + ret = -EIO; >>> + else >>> + *val = st->resp->sensor_odr.ret; >>> + break; >>> + default: >>> + break; >>> + } >>> + >>> + return ret; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read); >>> + >>> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, >>> + struct iio_chan_spec const *chan, >>> + int val, int val2, long mask) >>> +{ >>> + int ret = 0; >>> + >>> + switch (mask) { >>> + case IIO_CHAN_INFO_FREQUENCY: >>> + st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR; >>> + st->param.sensor_odr.data = val; >>> + >>> + /* Always roundup, so caller gets at least what it asks for. */ >>> + st->param.sensor_odr.roundup = 1; >>> + >>> + if (cros_ec_motion_send_host_cmd(st, 0)) >>> + ret = -EIO; >>> + break; >>> + case IIO_CHAN_INFO_SAMP_FREQ: >>> + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; >>> + st->param.ec_rate.data = val; >>> + >>> + if (cros_ec_motion_send_host_cmd(st, 0)) >>> + ret = -EIO; >>> + else >>> + st->curr_sampl_freq = val; >>> + break; >>> + default: >>> + ret = -EINVAL; >>> + break; >>> + } >>> + return ret; >>> +} >>> +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write); >>> + >>> +static int __maybe_unused cros_ec_sensors_prepare(struct device *dev) >>> +{ >>> + struct platform_device *pdev = to_platform_device(dev); >>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev); >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + >>> + if (st->curr_sampl_freq == 0) >>> + return 0; >>> + >>> + /* >>> + * If the sensors are sampled at high frequency, we will not be able to >>> + * sleep. Set to sampling to a long period if necessary. >>> + */ >>> + if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) { >>> + mutex_lock(&st->cmd_lock); >>> + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; >>> + st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY; >>> + cros_ec_motion_send_host_cmd(st, 0); >>> + mutex_unlock(&st->cmd_lock); >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static void __maybe_unused cros_ec_sensors_complete(struct device *dev) >>> +{ >>> + struct platform_device *pdev = to_platform_device(dev); >>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev); >>> + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); >>> + >>> + if (st->curr_sampl_freq == 0) >>> + return; >>> + >>> + if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) { >>> + mutex_lock(&st->cmd_lock); >>> + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; >>> + st->param.ec_rate.data = st->curr_sampl_freq; >>> + cros_ec_motion_send_host_cmd(st, 0); >>> + mutex_unlock(&st->cmd_lock); >>> + } >>> +} >>> + >>> +MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions"); >>> +MODULE_LICENSE("GPL v2"); >>> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h >>> new file mode 100644 >>> index 0000000..69e09dc >>> --- /dev/null >>> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h >>> @@ -0,0 +1,177 @@ >>> +/* >>> + * ChromeOS EC sensor hub >>> + * >>> + * Copyright (C) 2016 Google, Inc >>> + * >>> + * This software is licensed under the terms of the GNU General Public >>> + * License version 2, as published by the Free Software Foundation, and >>> + * may be copied, distributed, and modified under those terms. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + */ >>> + >>> +#ifndef __CROS_EC_SENSORS_CORE_H >>> +#define __CROS_EC_SENSORS_CORE_H >>> + >>> +#include <linux/irqreturn.h> >>> + >>> +enum { >>> + CROS_EC_SENSOR_X, >>> + CROS_EC_SENSOR_Y, >>> + CROS_EC_SENSOR_Z, >>> + CROS_EC_SENSOR_MAX_AXIS, >>> +}; >>> + >>> +/* EC returns sensor values using signed 16 bit registers */ >>> +#define CROS_EC_SENSOR_BITS 16 >>> + >>> +/* >>> + * 4 16 bit channels are allowed. >>> + * Good enough for current sensors, they use up to 3 16 bit vectors. >>> + */ >>> +#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2) >>> + >>> +/* Minimum sampling period to use when device is suspending */ >>> +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */ >>> + >>> +/** >>> + * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver >>> + * @ec: cros EC device structure >>> + * @cmd_lock: lock used to prevent simultaneous access to the >>> + * commands. >>> + * @msg: cros EC command structure >>> + * @param: motion sensor parameters structure >>> + * @resp: motion sensor response structure >>> + * @type: type of motion sensor >>> + * @loc: location where the motion sensor is placed >>> + * @calib: calibration parameters. Nothe that trigger >>> + * captured data will always provide the calibrated >>> + * data >>> + * @samples: static array to hold data from a single capture. >>> + * For each channel we need 2 bytees, except for >> typo : bytes >>> + * the timestamp. The timestamp is always last and >>> + * is always 8-byte aligned. >>> + * @read_ec_sensors_data: function used for accessing sensors values >>> + * @cuur_sampl_freq: current sampling period >>> + */ >>> +struct cros_ec_sensors_core_state { >>> + struct cros_ec_device *ec; >>> + struct mutex cmd_lock; >>> + >>> + struct cros_ec_command *msg; >>> + struct ec_params_motion_sense param; >>> + struct ec_response_motion_sense *resp; >>> + >>> + enum motionsensor_type type; >>> + enum motionsensor_location loc; >>> + >>> + struct calib_data { >>> + s16 offset; >>> + } calib[CROS_EC_SENSOR_MAX_AXIS]; >>> + >>> + u8 samples[CROS_EC_SAMPLE_SIZE]; >>> + >>> + int (*read_ec_sensors_data)(struct iio_dev *indio_dev, >>> + unsigned long scan_mask, s16 *data); >>> + >>> + int curr_sampl_freq; >>> +}; >>> + >>> +/** >>> + * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory >>> + * @indio_dev: pointer to IIO device >>> + * @scan_mask: bitmap of the sensor indices to scan >>> + * @data: location to store data >>> + * >>> + * This is the safe function for reading the EC data. It guarantees that the >>> + * data sampled was not modified by the EC while being read. >>> + * >>> + * Return: 0 on success, -errno on failure. >>> + */ >>> +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask, >>> + s16 *data); >>> + >>> +/** >>> + * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol >>> + * @indio_dev: pointer to IIO device >>> + * @scan_mask: bitmap of the sensor indices to scan >>> + * @data: location to store data >>> + * >>> + * Return: 0 on success, -errno on failure. >>> + */ >>> +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask, >>> + s16 *data); >>> + >>> +/** >>> + * cros_ec_sensors_core_init() - basic initialization of the core structure >>> + * @pdev: platform device created for the sensors >>> + * @indio_dev: iio device structure of the device >>> + * @physical_device: true if the device refers to a physical device >>> + * >>> + * Return: 0 on success, -errno on failure. >>> + */ >>> +int cros_ec_sensors_core_init(struct platform_device *pdev, >>> + struct iio_dev *indio_dev, bool physical_device); >>> + >>> +/** >>> + * cros_ec_sensors_capture() - the trigger handler function >>> + * @irq: the interrupt number. >>> + * @p: a pointer to the poll function. >>> + * >>> + * On a trigger event occurring, if the pollfunc is attached then this >>> + * handler is called as a threaded interrupt (and hence may sleep). It >>> + * is responsible for grabbing data from the device and pushing it into >>> + * the associated buffer. >>> + * >>> + * Return: IRQ_HANDLED >>> + */ >>> +irqreturn_t cros_ec_sensors_capture(int irq, void *p); >>> + >>> +/** >>> + * cros_ec_motion_send_host_cmd() - send motion sense host command >>> + * @st: pointer to state information for device >>> + * @opt_length: optional length to reduce the response size, useful on the data >>> + * path. Otherwise, the maximal allowed response size is used >>> + * >>> + * When called, the sub-command is assumed to be set in param->cmd. >>> + * >>> + * Return: 0 on success, -errno on failure. >>> + */ >>> +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st, >>> + u16 opt_length); >>> + >>> +/** >>> + * cros_ec_sensors_core_read() - function to request a value from the sensor >>> + * @st: pointer to state information for device >>> + * @chan: channel specification structure table >>> + * @val: will contain one element making up the returned value >>> + * @val2: will contain another element making up the returned value >>> + * @mask: specifies which values to be requested >>> + * >>> + * Return: the type of value returned by the device >>> + */ >>> +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, >>> + struct iio_chan_spec const *chan, >>> + int *val, int *val2, long mask); >>> + >>> +/** >>> + * cros_ec_sensors_core_write() - function to write a value to the sensor >>> + * @st: pointer to state information for device >>> + * @chan: channel specification structure table >>> + * @val: first part of value to write >>> + * @val2: second part of value to write >>> + * @mask: specifies which values to write >>> + * >>> + * Return: the type of value returned by the device >>> + */ >>> +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, >>> + struct iio_chan_spec const *chan, >>> + int val, int val2, long mask); >>> + >>> +/* List of extended channel specification for all sensors */ >>> +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; >>> + >>> +#endif /* __CROS_EC_SENSORS_CORE_H */ >>> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h >>> index d6539c1..7769ea6 100644 >>> --- a/include/linux/mfd/cros_ec.h >>> +++ b/include/linux/mfd/cros_ec.h >>> @@ -151,6 +151,15 @@ struct cros_ec_device { >>> int event_size; >>> }; >>> >>> +/** >>> + * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information >>> + * >>> + * @sensor_num: Id of the sensor, as reported by the EC. >>> + */ >>> +struct cros_ec_sensor_platform { >>> + u8 sensor_num; >>> +}; >>> + >>> /* struct cros_ec_platform - ChromeOS EC platform information >>> * >>> * @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...) >>> diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h >>> index 76728ff..8826e0f 100644 >>> --- a/include/linux/mfd/cros_ec_commands.h >>> +++ b/include/linux/mfd/cros_ec_commands.h >>> @@ -1315,6 +1315,24 @@ enum motionsense_command { >>> */ >>> MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5, >>> >>> + /* >>> + * Returns a single sensor data. >>> + */ >>> + MOTIONSENSE_CMD_DATA = 6, >>> + >>> + /* >>> + * Perform low level calibration.. On sensors that support it, ask to >>> + * do offset calibration. >>> + */ >>> + MOTIONSENSE_CMD_PERFORM_CALIB = 10, >>> + >>> + /* >>> + * Sensor Offset command is a setter/getter command for the offset used >>> + * for calibration. The offsets can be calculated by the host, or via >>> + * PERFORM_CALIB command. >>> + */ >>> + MOTIONSENSE_CMD_SENSOR_OFFSET = 11, >>> + >>> /* Number of motionsense sub-commands. */ >>> MOTIONSENSE_NUM_CMDS >>> }; >>> @@ -1335,12 +1353,18 @@ enum motionsensor_id { >>> enum motionsensor_type { >>> MOTIONSENSE_TYPE_ACCEL = 0, >>> MOTIONSENSE_TYPE_GYRO = 1, >>> + MOTIONSENSE_TYPE_MAG = 2, >>> + MOTIONSENSE_TYPE_PROX = 3, >>> + MOTIONSENSE_TYPE_LIGHT = 4, >>> + MOTIONSENSE_TYPE_ACTIVITY = 5, >>> + MOTIONSENSE_TYPE_MAX >>> }; >>> >>> /* List of motion sensor locations. */ >>> enum motionsensor_location { >>> MOTIONSENSE_LOC_BASE = 0, >>> MOTIONSENSE_LOC_LID = 1, >>> + MOTIONSENSE_LOC_MAX, >>> }; >>> >>> /* List of motion sensor chips. */ >>> @@ -1361,6 +1385,31 @@ enum motionsensor_chip { >>> */ >>> #define EC_MOTION_SENSE_NO_VALUE -1 >>> >>> +#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000 >>> + >>> +/* Set Calibration information */ >>> +#define MOTION_SENSE_SET_OFFSET 1 >>> + >>> +struct ec_response_motion_sensor_data { >>> + /* Flags for each sensor. */ >>> + uint8_t flags; >>> + /* Sensor number the data comes from */ >>> + uint8_t sensor_num; >>> + /* Each sensor is up to 3-axis. */ >>> + union { >>> + int16_t data[3]; >>> + struct { >>> + uint16_t rsvd; >>> + uint32_t timestamp; >>> + } __packed; >>> + struct { >>> + uint8_t activity; /* motionsensor_activity */ >>> + uint8_t state; >>> + int16_t add_info[2]; >>> + }; >>> + }; >>> +} __packed; >>> + >>> struct ec_params_motion_sense { >>> uint8_t cmd; >>> union { >>> @@ -1378,9 +1427,37 @@ struct ec_params_motion_sense { >>> int16_t data; >>> } ec_rate, kb_wake_angle; >>> >>> + /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ >>> + struct { >>> + uint8_t sensor_num; >>> + >>> + /* >>> + * bit 0: If set (MOTION_SENSE_SET_OFFSET), set >>> + * the calibration information in the EC. >>> + * If unset, just retrieve calibration information. >>> + */ >>> + uint16_t flags; >>> + >>> + /* >>> + * Temperature at calibration, in units of 0.01 C >>> + * 0x8000: invalid / unknown. >>> + * 0x0: 0C >>> + * 0x7fff: +327.67C >>> + */ >>> + int16_t temp; >>> + >>> + /* >>> + * Offset for calibration. >>> + * Unit: >>> + * Accelerometer: 1/1024 g >>> + * Gyro: 1/1024 deg/s >>> + * Compass: 1/16 uT >>> + */ >>> + int16_t offset[3]; >>> + } __packed sensor_offset; >>> + >>> /* Used for MOTIONSENSE_CMD_INFO. */ >>> struct { >>> - /* Should be element of enum motionsensor_id. */ >>> uint8_t sensor_num; >>> } info; >>> >>> @@ -1410,11 +1487,14 @@ struct ec_response_motion_sense { >>> /* Flags representing the motion sensor module. */ >>> uint8_t module_flags; >>> >>> - /* Flags for each sensor in enum motionsensor_id. */ >>> - uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT]; >>> + /* Number of sensors managed directly by the EC. */ >>> + uint8_t sensor_count; >>> >>> - /* Array of all sensor data. Each sensor is 3-axis. */ >>> - int16_t data[3*EC_MOTION_SENSOR_COUNT]; >>> + /* >>> + * Sensor data is truncated if response_max is too small >>> + * for holding all the data. >>> + */ >>> + struct ec_response_motion_sensor_data sensor[0]; >>> } dump; >>> >>> /* Used for MOTIONSENSE_CMD_INFO. */ >>> @@ -1429,6 +1509,9 @@ struct ec_response_motion_sense { >>> uint8_t chip; >>> } info; >>> >>> + /* Used for MOTIONSENSE_CMD_DATA */ >>> + struct ec_response_motion_sensor_data data; >>> + >>> /* >>> * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR, >>> * MOTIONSENSE_CMD_SENSOR_RANGE, and >>> @@ -1438,6 +1521,12 @@ struct ec_response_motion_sense { >>> /* Current value of the parameter queried. */ >>> int32_t ret; >>> } ec_rate, sensor_odr, sensor_range, kb_wake_angle; >>> + >>> + /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ >>> + struct { >>> + int16_t temp; >>> + int16_t offset[3]; >>> + } sensor_offset, perform_calib; >>> }; >>> } __packed; >>> >>> >> > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html