// SPDX-License-Identifier: GPL-2.0
/*
* cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
*
* Copyright (C) 2016 Google, Inc
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/cros_ec_sensors_core.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_data/cros_ec_sensorhub.h>
#include <linux/platform_device.h>
/*
* Hard coded to the first device to support sensor fifo. The EC has a 2048
* byte fifo and will trigger an interrupt when fifo is 2/3 full.
*/
#define CROS_EC_FIFO_SIZE (2048 * 2 / 3)
static char *cros_ec_loc[] = {
[MOTIONSENSE_LOC_BASE] = "base",
[MOTIONSENSE_LOC_LID] = "lid",
[MOTIONSENSE_LOC_MAX] = "unknown",
};
static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
u16 cmd_offset, u16 cmd, u32 *mask)
{
int ret;
struct {
struct cros_ec_command msg;
union {
struct ec_params_get_cmd_versions params;
struct ec_response_get_cmd_versions resp;
};
} __packed buf = {
.msg = {
.command = EC_CMD_GET_CMD_VERSIONS + cmd_offset,
.insize = sizeof(struct ec_response_get_cmd_versions),
.outsize = sizeof(struct ec_params_get_cmd_versions)
},
.params = {.cmd = cmd}
};
ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg);
if (ret >= 0)
*mask = buf.resp.version_mask;
return ret;
}
static void get_default_min_max_freq(enum motionsensor_type type,
u32 *min_freq,
u32 *max_freq,
u32 *max_fifo_events)
{
/*
* We don't know fifo size, set to size previously used by older
* hardware.
*/
*max_fifo_events = CROS_EC_FIFO_SIZE;
switch (type) {
case MOTIONSENSE_TYPE_ACCEL:
*min_freq = 12500;
*max_freq = 100000;
break;
case MOTIONSENSE_TYPE_GYRO:
*min_freq = 25000;
*max_freq = 100000;
break;
case MOTIONSENSE_TYPE_MAG:
*min_freq = 5000;
*max_freq = 25000;
break;
case MOTIONSENSE_TYPE_PROX:
case MOTIONSENSE_TYPE_LIGHT:
*min_freq = 100;
*max_freq = 50000;
break;
case MOTIONSENSE_TYPE_BARO:
*min_freq = 250;
*max_freq = 20000;
break;
case MOTIONSENSE_TYPE_ACTIVITY:
default:
*min_freq = 0;
*max_freq = 0;
break;
}
}
static int cros_ec_sensor_set_ec_rate(struct cros_ec_sensors_core_state *st,
int rate)
{
int ret;
if (rate > U16_MAX)
rate = U16_MAX;
mutex_lock(&st->cmd_lock);
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
st->param.ec_rate.data = rate;
ret = cros_ec_motion_send_host_cmd(st, 0);
mutex_unlock(&st->cmd_lock);
return ret;
}
static ssize_t cros_ec_sensor_set_report_latency(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
int integer, fract, ret;
int latency;
ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract);
if (ret)
return ret;
/* EC rate is in ms. */
latency = integer * 1000 + fract / 1000;
ret = cros_ec_sensor_set_ec_rate(st, latency);
if (ret < 0)
return ret;
return len;
}
static ssize_t cros_ec_sensor_get_report_latency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
int latency, ret;
mutex_lock(&st->cmd_lock);
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
st->param.ec_rate.data = EC_MOTION_SENSE_NO_VALUE;
ret = cros_ec_motion_send_host_cmd(st, 0);
latency = st->resp->ec_rate.ret;
mutex_unlock(&st->cmd_lock);
if (ret < 0)
return ret;
return sprintf(buf, "%d.%06u\n",
latency / 1000,
(latency % 1000) * 1000);
}
static IIO_DEVICE_ATTR(hwfifo_timeout, 0644,
cros_ec_sensor_get_report_latency,
cros_ec_sensor_set_report_latency, 0);
static ssize_t hwfifo_watermark_max_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev =