Merge branch 'ib-iio-mfd-4.9rc1' into togreg
Immutable branch to allow mfd changes to also be available in Lee's MFD tree.
This commit is contained in:
commit
7f2d496b5e
18
Documentation/ABI/testing/sysfs-bus-iio-cros-ec
Normal file
18
Documentation/ABI/testing/sysfs-bus-iio-cros-ec
Normal file
@ -0,0 +1,18 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/calibrate
|
||||
Date: July 2015
|
||||
KernelVersion: 4.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Writing '1' will perform a FOC (Fast Online Calibration). The
|
||||
corresponding calibration offsets can be read from *_calibbias
|
||||
entries.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/location
|
||||
Date: July 2015
|
||||
KernelVersion: 4.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
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. Current valid
|
||||
values are 'base' and 'lid'.
|
@ -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"
|
||||
|
@ -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/
|
||||
|
22
drivers/iio/common/cros_ec_sensors/Kconfig
Normal file
22
drivers/iio/common/cros_ec_sensors/Kconfig
Normal file
@ -0,0 +1,22 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
config IIO_CROS_EC_SENSORS
|
||||
tristate "ChromeOS EC Contiguous Sensors"
|
||||
depends on IIO_CROS_EC_SENSORS_CORE
|
||||
help
|
||||
Module to handle 3d contiguous sensors like
|
||||
Accelerometers, Gyroscope and Magnetometer that are
|
||||
presented by the ChromeOS EC Sensor hub.
|
||||
Creates an IIO device for each functions.
|
6
drivers/iio/common/cros_ec_sensors/Makefile
Normal file
6
drivers/iio/common/cros_ec_sensors/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for sensors seen through the ChromeOS EC sensor hub.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
|
322
drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
Normal file
322
drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This driver uses the cros-ec interface to communicate with the Chrome OS
|
||||
* EC about sensors data. Data access is presented through iio sysfs.
|
||||
*/
|
||||
|
||||
#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/iio/triggered_buffer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "cros_ec_sensors_core.h"
|
||||
|
||||
#define CROS_EC_SENSORS_MAX_CHANNELS 4
|
||||
|
||||
/* State data for ec_sensors iio driver. */
|
||||
struct cros_ec_sensors_state {
|
||||
/* Shared by all sensors */
|
||||
struct cros_ec_sensors_core_state core;
|
||||
|
||||
struct iio_chan_spec channels[CROS_EC_SENSORS_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static int cros_ec_sensors_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
|
||||
s16 data = 0;
|
||||
s64 val64;
|
||||
int i;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = st->core.read_ec_sensors_data(indio_dev, 1 << idx, &data);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
*val = data;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
st->core.param.sensor_offset.flags = 0;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.calib[i] =
|
||||
st->core.resp->sensor_offset.offset[i];
|
||||
|
||||
*val = st->core.calib[idx];
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
val64 = st->core.resp->sensor_range.ret;
|
||||
switch (st->core.type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
/*
|
||||
* EC returns data in g, iio exepects m/s^2.
|
||||
* Do not use IIO_G_TO_M_S_2 to avoid precision loss.
|
||||
*/
|
||||
*val = div_s64(val64 * 980665, 10);
|
||||
*val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
/*
|
||||
* EC returns data in dps, iio expects rad/s.
|
||||
* Do not use IIO_DEGREE_TO_RAD to avoid precision
|
||||
* loss. Round to the nearest integer.
|
||||
*/
|
||||
*val = div_s64(val64 * 314159 + 9000000ULL, 1000);
|
||||
*val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
/*
|
||||
* EC returns data in 16LSB / uT,
|
||||
* iio expects Gauss
|
||||
*/
|
||||
*val = val64;
|
||||
*val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
|
||||
mask);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->core.cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
|
||||
int i;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.calib[idx] = val;
|
||||
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
st->core.param.sensor_offset.flags =
|
||||
MOTION_SENSE_SET_OFFSET;
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.param.sensor_offset.offset[i] =
|
||||
st->core.calib[i];
|
||||
st->core.param.sensor_offset.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (st->core.type == MOTIONSENSE_TYPE_MAG) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = val;
|
||||
|
||||
/* Always roundup, so caller gets at least what it asks for. */
|
||||
st->core.param.sensor_range.roundup = 1;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_write(
|
||||
&st->core, chan, val, val2, mask);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->core.cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ec_sensors_info = {
|
||||
.read_raw = &cros_ec_sensors_read,
|
||||
.write_raw = &cros_ec_sensors_write,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_device *ec_device;
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_sensors_state *state;
|
||||
struct iio_chan_spec *channel;
|
||||
int ret, i;
|
||||
|
||||
if (!ec_dev || !ec_dev->ec_dev) {
|
||||
dev_warn(&pdev->dev, "No CROS EC device found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ec_device = ec_dev->ec_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &ec_sensors_info;
|
||||
state = iio_priv(indio_dev);
|
||||
for (channel = state->channels, i = CROS_EC_SENSOR_X;
|
||||
i < CROS_EC_SENSOR_MAX_AXIS; i++, channel++) {
|
||||
/* Common part */
|
||||
channel->info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS);
|
||||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_index = i;
|
||||
channel->ext_info = cros_ec_sensors_ext_info;
|
||||
channel->modified = 1;
|
||||
channel->channel2 = IIO_MOD_X + i;
|
||||
channel->scan_type.sign = 's';
|
||||
|
||||
/* Sensor specific */
|
||||
switch (state->core.type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
channel->type = IIO_ACCEL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
channel->type = IIO_ANGL_VEL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
channel->type = IIO_MAGN;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unknown motion sensor\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Timestamp */
|
||||
channel->type = IIO_TIMESTAMP;
|
||||
channel->channel = -1;
|
||||
channel->scan_index = CROS_EC_SENSOR_MAX_AXIS;
|
||||
channel->scan_type.sign = 's';
|
||||
channel->scan_type.realbits = 64;
|
||||
channel->scan_type.storagebits = 64;
|
||||
|
||||
indio_dev->channels = state->channels;
|
||||
indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS;
|
||||
|
||||
/* There is only enough room for accel and gyro in the io space */
|
||||
if ((state->core.ec->cmd_readmem != NULL) &&
|
||||
(state->core.type != MOTIONSENSE_TYPE_MAG))
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
|
||||
else
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninit_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_uninit_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_sensors_ids[] = {
|
||||
{
|
||||
.name = "cros-ec-accel",
|
||||
},
|
||||
{
|
||||
.name = "cros-ec-gyro",
|
||||
},
|
||||
{
|
||||
.name = "cros-ec-mag",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
|
||||
|
||||
static struct platform_driver cros_ec_sensors_platform_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-sensors",
|
||||
},
|
||||
.probe = cros_ec_sensors_probe,
|
||||
.remove = cros_ec_sensors_remove,
|
||||
.id_table = cros_ec_sensors_ids,
|
||||
};
|
||||
module_platform_driver(cros_ec_sensors_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
|
||||
MODULE_LICENSE("GPL v2");
|
450
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
Normal file
450
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
* 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] = st->resp->perform_calib.offset[i];
|
||||
}
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
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 = "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);
|
||||
|
||||
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);
|
||||
|
||||
if (ret >= 0)
|
||||
*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;
|
||||
|
||||
/* 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(indio_dev));
|
||||
|
||||
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);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
|
||||
MODULE_LICENSE("GPL v2");
|
175
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
Normal file
175
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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. Note 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 bytes, except for
|
||||
* 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;
|
||||
|
||||
s16 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 */
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
@ -87,6 +88,41 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
|
||||
{
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
if (ec->features[0] == -1U && ec->features[1] == -1U) {
|
||||
/* features bitmap not read yet */
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
|
||||
msg->insize = sizeof(ec->features);
|
||||
msg->outsize = 0;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
|
||||
dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
|
||||
ret, msg->result);
|
||||
memset(ec->features, 0, sizeof(ec->features));
|
||||
}
|
||||
|
||||
memcpy(ec->features, msg->data, sizeof(ec->features));
|
||||
|
||||
dev_dbg(ec->dev, "EC features %08x %08x\n",
|
||||
ec->features[0], ec->features[1]);
|
||||
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
|
||||
}
|
||||
|
||||
/* Device file ops */
|
||||
static int ec_device_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
@ -230,6 +266,123 @@ static void __remove(struct device *dev)
|
||||
kfree(ec);
|
||||
}
|
||||
|
||||
static void cros_ec_sensors_register(struct cros_ec_dev *ec)
|
||||
{
|
||||
/*
|
||||
* Issue a command to get the number of sensor reported.
|
||||
* Build an array of sensors driver and register them all.
|
||||
*/
|
||||
int ret, i, id, sensor_num;
|
||||
struct mfd_cell *sensor_cells;
|
||||
struct cros_ec_sensor_platform *sensor_platforms;
|
||||
int sensor_type[MOTIONSENSE_TYPE_MAX];
|
||||
struct ec_params_motion_sense *params;
|
||||
struct ec_response_motion_sense *resp;
|
||||
struct cros_ec_command *msg;
|
||||
|
||||
msg = kzalloc(sizeof(struct cros_ec_command) +
|
||||
max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
|
||||
if (msg == NULL)
|
||||
return;
|
||||
|
||||
msg->version = 2;
|
||||
msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
|
||||
msg->outsize = sizeof(*params);
|
||||
msg->insize = sizeof(*resp);
|
||||
|
||||
params = (struct ec_params_motion_sense *)msg->data;
|
||||
params->cmd = MOTIONSENSE_CMD_DUMP;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
|
||||
dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
|
||||
ret, msg->result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
resp = (struct ec_response_motion_sense *)msg->data;
|
||||
sensor_num = resp->dump.sensor_count;
|
||||
/* Allocate 2 extra sensors in case lid angle or FIFO are needed */
|
||||
sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
|
||||
GFP_KERNEL);
|
||||
if (sensor_cells == NULL)
|
||||
goto error;
|
||||
|
||||
sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
|
||||
(sensor_num + 1), GFP_KERNEL);
|
||||
if (sensor_platforms == NULL)
|
||||
goto error_platforms;
|
||||
|
||||
memset(sensor_type, 0, sizeof(sensor_type));
|
||||
id = 0;
|
||||
for (i = 0; i < sensor_num; i++) {
|
||||
params->cmd = MOTIONSENSE_CMD_INFO;
|
||||
params->info.sensor_num = i;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
|
||||
dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
|
||||
i, ret, msg->result);
|
||||
continue;
|
||||
}
|
||||
switch (resp->info.type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
sensor_cells[id].name = "cros-ec-accel";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
sensor_cells[id].name = "cros-ec-gyro";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
sensor_cells[id].name = "cros-ec-mag";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_PROX:
|
||||
sensor_cells[id].name = "cros-ec-prox";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_LIGHT:
|
||||
sensor_cells[id].name = "cros-ec-light";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_ACTIVITY:
|
||||
sensor_cells[id].name = "cros-ec-activity";
|
||||
break;
|
||||
default:
|
||||
dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
|
||||
continue;
|
||||
}
|
||||
sensor_platforms[id].sensor_num = i;
|
||||
sensor_cells[id].id = sensor_type[resp->info.type];
|
||||
sensor_cells[id].platform_data = &sensor_platforms[id];
|
||||
sensor_cells[id].pdata_size =
|
||||
sizeof(struct cros_ec_sensor_platform);
|
||||
|
||||
sensor_type[resp->info.type]++;
|
||||
id++;
|
||||
}
|
||||
if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
|
||||
sensor_platforms[id].sensor_num = sensor_num;
|
||||
|
||||
sensor_cells[id].name = "cros-ec-angle";
|
||||
sensor_cells[id].id = 0;
|
||||
sensor_cells[id].platform_data = &sensor_platforms[id];
|
||||
sensor_cells[id].pdata_size =
|
||||
sizeof(struct cros_ec_sensor_platform);
|
||||
id++;
|
||||
}
|
||||
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
|
||||
sensor_cells[id].name = "cros-ec-ring";
|
||||
id++;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
dev_err(ec->dev, "failed to add EC sensors\n");
|
||||
|
||||
kfree(sensor_platforms);
|
||||
error_platforms:
|
||||
kfree(sensor_cells);
|
||||
error:
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
static int ec_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
int retval = -ENOMEM;
|
||||
@ -245,6 +398,8 @@ static int ec_device_probe(struct platform_device *pdev)
|
||||
ec->ec_dev = dev_get_drvdata(dev->parent);
|
||||
ec->dev = dev;
|
||||
ec->cmd_offset = ec_platform->cmd_offset;
|
||||
ec->features[0] = -1U; /* Not cached yet */
|
||||
ec->features[1] = -1U; /* Not cached yet */
|
||||
device_initialize(&ec->class_dev);
|
||||
cdev_init(&ec->cdev, &fops);
|
||||
|
||||
@ -282,6 +437,10 @@ static int ec_device_probe(struct platform_device *pdev)
|
||||
goto dev_reg_failed;
|
||||
}
|
||||
|
||||
/* check whether this EC is a sensor hub. */
|
||||
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
|
||||
cros_ec_sensors_register(ec);
|
||||
|
||||
return 0;
|
||||
|
||||
dev_reg_failed:
|
||||
|
@ -148,6 +148,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', ...)
|
||||
@ -175,6 +184,7 @@ struct cros_ec_dev {
|
||||
struct cros_ec_device *ec_dev;
|
||||
struct device *dev;
|
||||
u16 cmd_offset;
|
||||
u32 features[2];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -713,6 +713,90 @@ struct ec_response_get_set_value {
|
||||
/* More than one command can use these structs to get/set paramters. */
|
||||
#define EC_CMD_GSV_PAUSE_IN_S5 0x0c
|
||||
|
||||
/*****************************************************************************/
|
||||
/* List the features supported by the firmware */
|
||||
#define EC_CMD_GET_FEATURES 0x0d
|
||||
|
||||
/* Supported features */
|
||||
enum ec_feature_code {
|
||||
/*
|
||||
* This image contains a limited set of features. Another image
|
||||
* in RW partition may support more features.
|
||||
*/
|
||||
EC_FEATURE_LIMITED = 0,
|
||||
/*
|
||||
* Commands for probing/reading/writing/erasing the flash in the
|
||||
* EC are present.
|
||||
*/
|
||||
EC_FEATURE_FLASH = 1,
|
||||
/*
|
||||
* Can control the fan speed directly.
|
||||
*/
|
||||
EC_FEATURE_PWM_FAN = 2,
|
||||
/*
|
||||
* Can control the intensity of the keyboard backlight.
|
||||
*/
|
||||
EC_FEATURE_PWM_KEYB = 3,
|
||||
/*
|
||||
* Support Google lightbar, introduced on Pixel.
|
||||
*/
|
||||
EC_FEATURE_LIGHTBAR = 4,
|
||||
/* Control of LEDs */
|
||||
EC_FEATURE_LED = 5,
|
||||
/* Exposes an interface to control gyro and sensors.
|
||||
* The host goes through the EC to access these sensors.
|
||||
* In addition, the EC may provide composite sensors, like lid angle.
|
||||
*/
|
||||
EC_FEATURE_MOTION_SENSE = 6,
|
||||
/* The keyboard is controlled by the EC */
|
||||
EC_FEATURE_KEYB = 7,
|
||||
/* The AP can use part of the EC flash as persistent storage. */
|
||||
EC_FEATURE_PSTORE = 8,
|
||||
/* The EC monitors BIOS port 80h, and can return POST codes. */
|
||||
EC_FEATURE_PORT80 = 9,
|
||||
/*
|
||||
* Thermal management: include TMP specific commands.
|
||||
* Higher level than direct fan control.
|
||||
*/
|
||||
EC_FEATURE_THERMAL = 10,
|
||||
/* Can switch the screen backlight on/off */
|
||||
EC_FEATURE_BKLIGHT_SWITCH = 11,
|
||||
/* Can switch the wifi module on/off */
|
||||
EC_FEATURE_WIFI_SWITCH = 12,
|
||||
/* Monitor host events, through for example SMI or SCI */
|
||||
EC_FEATURE_HOST_EVENTS = 13,
|
||||
/* The EC exposes GPIO commands to control/monitor connected devices. */
|
||||
EC_FEATURE_GPIO = 14,
|
||||
/* The EC can send i2c messages to downstream devices. */
|
||||
EC_FEATURE_I2C = 15,
|
||||
/* Command to control charger are included */
|
||||
EC_FEATURE_CHARGER = 16,
|
||||
/* Simple battery support. */
|
||||
EC_FEATURE_BATTERY = 17,
|
||||
/*
|
||||
* Support Smart battery protocol
|
||||
* (Common Smart Battery System Interface Specification)
|
||||
*/
|
||||
EC_FEATURE_SMART_BATTERY = 18,
|
||||
/* EC can dectect when the host hangs. */
|
||||
EC_FEATURE_HANG_DETECT = 19,
|
||||
/* Report power information, for pit only */
|
||||
EC_FEATURE_PMU = 20,
|
||||
/* Another Cros EC device is present downstream of this one */
|
||||
EC_FEATURE_SUB_MCU = 21,
|
||||
/* Support USB Power delivery (PD) commands */
|
||||
EC_FEATURE_USB_PD = 22,
|
||||
/* Control USB multiplexer, for audio through USB port for instance. */
|
||||
EC_FEATURE_USB_MUX = 23,
|
||||
/* Motion Sensor code has an internal software FIFO */
|
||||
EC_FEATURE_MOTION_SENSE_FIFO = 24,
|
||||
};
|
||||
|
||||
#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
|
||||
#define EC_FEATURE_MASK_1(event_code) (1UL << (event_code - 32))
|
||||
struct ec_response_get_features {
|
||||
uint32_t flags[2];
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Flash commands */
|
||||
@ -1315,6 +1399,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 +1437,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 +1469,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 +1511,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 +1571,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 +1593,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 +1605,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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user