mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
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
11 changed files with 1342 additions and 5 deletions
|
|
@ -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 */
|
||||
Loading…
Reference in a new issue