Commit 413a103c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tag-chrome-platform-for-v5.7' of...

Merge tag 'tag-chrome-platform-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux

Pull chrome platform updates from Benson Leung:

  cros-usbpd-notify and cros_ec_typec:
   - Add a new notification driver that handles and dispatches USB PD
     related events to other drivers.
   - Add a Type C connector class driver for cros_ec

  CrOS EC:
   - Introduce a new cros_ec_cmd_xfer_status helper

  Sensors/iio:
   - A series from Gwendal that adds Cros EC sensor hub FIFO support

  Wilco EC:
   - Fix a build warning.
   - Platform data shouldn't include kernel.h

  Misc:
   - i2c api conversion complete, with i2c_new_client_device instead of
     i2c_new_device in chromeos_laptop.
   - Replace zero-length array with flexible-array member in
     cros_ec_chardev and wilco_ec
   - Update new structure for SPI transfer delays in cros_ec_spi

* tag 'tag-chrome-platform-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux: (34 commits)
  platform/chrome: cros_ec_spi: Wait for USECS, not NSECS
  iio: cros_ec: Use Hertz as unit for sampling frequency
  iio: cros_ec: Report hwfifo_watermark_max
  iio: cros_ec: Expose hwfifo_timeout
  iio: cros_ec: Remove pm function
  iio: cros_ec: Register to cros_ec_sensorhub when EC supports FIFO
  iio: expose iio_device_set_clock
  iio: cros_ec: Move function description to .c file
  platform/chrome: cros_ec_sensorhub: Add median filter
  platform/chrome: cros_ec_sensorhub: Add code to spread timestmap
  platform/chrome: cros_ec_sensorhub: Add FIFO support
  platform/chrome: cros_ec_sensorhub: Add the number of sensors in sensorhub
  platform/chrome: chromeos_laptop: make I2C API conversion complete
  platform/chrome: wilco_ec: event: Replace zero-length array with flexible-array member
  platform/chrome: cros_ec_chardev: Replace zero-length array with flexible-array member
  platform/chrome: cros_ec_typec: Update port info from EC
  platform/chrome: Add Type C connector class driver
  platform/chrome: cros_usbpd_notify: Pull PD_HOST_EVENT status
  platform/chrome: cros_usbpd_notify: Amend ACPI driver to plat
  platform/chrome: cros_usbpd_notify: Add driver data struct
  ...
parents 9b06860d a4638771
......@@ -170,7 +170,8 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
......@@ -190,11 +191,6 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
state->sign[CROS_EC_SENSOR_Z] = -1;
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
......
......@@ -97,7 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev)
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, NULL);
if (ret)
return ret;
......@@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids);
static struct platform_driver cros_ec_lid_angle_platform_driver = {
.driver = {
.name = DRV_NAME,
.pm = &cros_ec_sensors_pm_ops,
},
.probe = cros_ec_lid_angle_probe,
.id_table = cros_ec_lid_angle_ids,
......
......@@ -230,10 +230,14 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
cros_ec_sensors_capture,
cros_ec_sensors_push_data);
if (ret)
return ret;
iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
indio_dev->info = &ec_sensors_info;
state = iio_priv(indio_dev);
for (channel = state->channels, i = CROS_EC_SENSOR_X;
......@@ -245,7 +249,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
BIT(IIO_CHAN_INFO_CALIBSCALE);
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_FREQUENCY) |
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ);
......@@ -292,11 +295,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
else
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
......@@ -317,7 +315,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
static struct platform_driver cros_ec_sensors_platform_driver = {
.driver = {
.name = "cros-ec-sensors",
.pm = &cros_ec_sensors_pm_ops,
},
.probe = cros_ec_sensors_probe,
.id_table = cros_ec_sensors_ids,
......
......@@ -11,7 +11,9 @@
#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>
......@@ -20,6 +22,12 @@
#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",
......@@ -53,8 +61,15 @@ static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
static void get_default_min_max_freq(enum motionsensor_type type,
u32 *min_freq,
u32 *max_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:
case MOTIONSENSE_TYPE_GYRO:
......@@ -82,9 +97,155 @@ static void get_default_min_max_freq(enum motionsensor_type type,
}
}
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 = dev_to_iio_dev(dev);
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
return sprintf(buf, "%d\n", st->fifo_max_event_count);
}
static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0);
const struct attribute *cros_ec_sensor_fifo_attributes[] = {
&iio_dev_attr_hwfifo_timeout.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
NULL,
};
EXPORT_SYMBOL_GPL(cros_ec_sensor_fifo_attributes);
int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
s16 *data,
s64 timestamp)
{
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
s16 *out;
s64 delta;
unsigned int i;
/*
* Ignore samples if the buffer is not set: it is needed if the ODR is
* set but the buffer is not enabled yet.
*/
if (!iio_buffer_enabled(indio_dev))
return 0;
out = (s16 *)st->samples;
for_each_set_bit(i,
indio_dev->active_scan_mask,
indio_dev->masklength) {
*out = data[i];
out++;
}
if (iio_device_get_clock(indio_dev) != CLOCK_BOOTTIME)
delta = iio_get_time_ns(indio_dev) - cros_ec_get_time_ns();
else
delta = 0;
iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
timestamp + delta);
return 0;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_push_data);
static void cros_ec_sensors_core_clean(void *arg)
{
struct platform_device *pdev = (struct platform_device *)arg;
struct cros_ec_sensorhub *sensor_hub =
dev_get_drvdata(pdev->dev.parent);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
u8 sensor_num = st->param.info.sensor_num;
cros_ec_sensorhub_unregister_push_data(sensor_hub, sensor_num);
}
/**
* 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
* @trigger_capture: function pointer to call buffer is triggered,
* for backward compatibility.
* @push_data: function to call when cros_ec_sensorhub receives
* a sample for that sensor.
*
* 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)
bool physical_device,
cros_ec_sensors_capture_t trigger_capture,
cros_ec_sensorhub_push_data_cb_t push_data)
{
struct device *dev = &pdev->dev;
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
......@@ -92,6 +253,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
struct cros_ec_dev *ec = sensor_hub->ec;
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
u32 ver_mask;
int frequencies[ARRAY_SIZE(state->frequencies) / 2] = { 0 };
int ret, i;
platform_set_drvdata(pdev, indio_dev);
......@@ -123,8 +285,6 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
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;
ret = cros_ec_motion_send_host_cmd(state, 0);
......@@ -142,16 +302,63 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
state->calib[i].scale = MOTION_SENSE_DEFAULT_SCALE;
/* 0 is a correct value used to stop the device */
state->frequencies[0] = 0;
if (state->msg->version < 3) {
get_default_min_max_freq(state->resp->info.type,
&state->frequencies[1],
&state->frequencies[2]);
&frequencies[1],
&frequencies[2],
&state->fifo_max_event_count);
} else {
state->frequencies[1] =
state->resp->info_3.min_frequency;
state->frequencies[2] =
state->resp->info_3.max_frequency;
frequencies[1] = state->resp->info_3.min_frequency;
frequencies[2] = state->resp->info_3.max_frequency;
state->fifo_max_event_count =
state->resp->info_3.fifo_max_event_count;
}
for (i = 0; i < ARRAY_SIZE(frequencies); i++) {
state->frequencies[2 * i] = frequencies[i] / 1000;
state->frequencies[2 * i + 1] =
(frequencies[i] % 1000) * 1000;
}
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
/*
* Create a software buffer, feed by the EC FIFO.
* We can not use trigger here, as events are generated
* as soon as sample_frequency is set.
*/
struct iio_buffer *buffer;
buffer = devm_iio_kfifo_allocate(dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
ret = cros_ec_sensorhub_register_push_data(
sensor_hub, sensor_platform->sensor_num,
indio_dev, push_data);
if (ret)
return ret;
ret = devm_add_action_or_reset(
dev, cros_ec_sensors_core_clean, pdev);
if (ret)
return ret;
/* Timestamp coming from FIFO are in ns since boot. */
ret = iio_device_set_clock(indio_dev, CLOCK_BOOTTIME);
if (ret)
return ret;
} else {
/*
* The only way to get samples in buffer is to set a
* software tigger (systrig, hrtimer).
*/
ret = devm_iio_triggered_buffer_setup(
dev, indio_dev, NULL, trigger_capture,
NULL);
if (ret)
return ret;
}
}
......@@ -159,6 +366,16 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
/**
* cros_ec_motion_send_host_cmd() - send motion sense host command
* @state: 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 *state,
u16 opt_length)
{
......@@ -421,6 +638,14 @@ int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
/**
* 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)
{
......@@ -445,6 +670,18 @@ int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
/**
* 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)
{
struct iio_poll_func *pf = p;
......@@ -480,26 +717,24 @@ irqreturn_t cros_ec_sensors_capture(int irq, void *p)
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
/**
* 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)
{
int ret;
int ret, frequency;
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;
ret = cros_ec_motion_send_host_cmd(st, 0);
if (ret)
break;
*val = st->resp->ec_rate.ret;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_FREQUENCY:
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
st->param.sensor_odr.data =
EC_MOTION_SENSE_NO_VALUE;
......@@ -508,8 +743,10 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
if (ret)
break;
*val = st->resp->sensor_odr.ret;
ret = IIO_VAL_INT;
frequency = st->resp->sensor_odr.ret;
*val = frequency / 1000;
*val2 = (frequency % 1000) * 1000;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
default:
ret = -EINVAL;
......@@ -520,6 +757,17 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
/**
* cros_ec_sensors_core_read_avail() - get available values
* @indio_dev: pointer to state information for device
* @chan: channel specification structure table
* @vals: list of available values
* @type: type of data returned
* @length: number of data returned in the array
* @mask: specifies which values to be requested
*
* Return: an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
*/
int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals,
......@@ -533,7 +781,7 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SAMP_FREQ:
*length = ARRAY_SIZE(state->frequencies);
*vals = (const int *)&state->frequencies;
*type = IIO_VAL_INT;
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
}
......@@ -541,31 +789,33 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
/**
* 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)
{
int ret;
int ret, frequency;
switch (mask) {
case IIO_CHAN_INFO_FREQUENCY:
case IIO_CHAN_INFO_SAMP_FREQ:
frequency = val * 1000 + val2 / 1000;
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
st->param.sensor_odr.data = val;
st->param.sensor_odr.data = frequency;
/* Always roundup, so caller gets at least what it asks for. */
st->param.sensor_odr.roundup = 1;
ret = cros_ec_motion_send_host_cmd(st, 0);
break;
case IIO_CHAN_INFO_SAMP_FREQ:
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
st->param.ec_rate.data = val;
ret = cros_ec_motion_send_host_cmd(st, 0);
if (ret)
break;
st->curr_sampl_freq = val;
break;
default:
ret = -EINVAL;
break;
......@@ -574,52 +824,5 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
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 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 iio_dev *indio_dev = dev_get_drvdata(dev);
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);
}
}
const struct dev_pm_ops cros_ec_sensors_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.prepare = cros_ec_sensors_prepare,
.complete = cros_ec_sensors_complete
#endif
};
EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
MODULE_LICENSE("GPL v2");
......@@ -189,7 +189,12 @@ ssize_t iio_read_const_attr(struct device *dev,
}
EXPORT_SYMBOL(iio_read_const_attr);
static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
/**
* iio_device_set_clock() - Set current timestamping clock for the device
* @indio_dev: IIO device structure containing the device
* @clock_id: timestamping clock posix identifier to set.
*/
int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
{
int ret;
const struct iio_event_interface *ev_int = indio_dev->event_interface;
......@@ -207,6 +212,7 @@ static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
return 0;
}
EXPORT_SYMBOL(iio_device_set_clock);
/**
* iio_get_time_ns() - utility function to get a time stamp for events etc
......
......@@ -177,10 +177,14 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
cros_ec_sensors_capture,
cros_ec_sensors_push_data);
if (ret)
return ret;
iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
indio_dev->info = &cros_ec_light_prox_info;
state = iio_priv(indio_dev);
state->core.type = state->core.resp->info.type;
......@@ -189,8 +193,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
/* Common part */
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_FREQUENCY);
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
......@@ -236,11 +239,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
......@@ -258,7 +256,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
static struct platform_driver cros_ec_light_prox_platform_driver = {
.driver = {
.name = "cros-ec-light-prox",
.pm = &cros_ec_sensors_pm_ops,
},
.probe = cros_ec_light_prox_probe,
.id_table = cros_ec_light_prox_ids,
......
......@@ -134,10 +134,14 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
cros_ec_sensors_capture,
cros_ec_sensors_push_data);
if (ret)
return ret;
iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
indio_dev->info = &cros_ec_baro_info;
state = iio_priv(indio_dev);
state->core.type = state->core.resp->info.type;
......@@ -147,8 +151,7 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_FREQUENCY);
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
......@@ -182,11 +185,6 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
......
......@@ -7,7 +7,7 @@ config MFD_CROS_EC
tristate "Platform support for Chrome hardware (transitional)"
select CHROME_PLATFORMS
select CROS_EC
select CONFIG_MFD_CROS_EC_DEV
select MFD_CROS_EC_DEV
depends on X86 || ARM || ARM64 || COMPILE_TEST
help
This is a transitional Kconfig option and will be removed after
......@@ -214,6 +214,17 @@ config CROS_EC_SYSFS
To compile this driver as a module, choose M here: the
module will be called cros_ec_sysfs.
config CROS_EC_TYPEC
tristate "ChromeOS EC Type-C Connector Control"
depends on MFD_CROS_EC_DEV && TYPEC
default MFD_CROS_EC_DEV
help
If you say Y here, you get support for accessing Type C connector
information from the Chrome OS EC.
To compile this driver as a module, choose M here: the module will be
called cros_ec_typec.
config CROS_USBPD_LOGGER
tristate "Logging driver for USB PD charger"
depends on CHARGER_CROS_USBPD
......@@ -226,6 +237,20 @@ config CROS_USBPD_LOGGER
To compile this driver as a module, choose M here: the
module will be called cros_usbpd_logger.
config CROS_USBPD_NOTIFY
tristate "ChromeOS Type-C power delivery event notifier"
depends on MFD_CROS_EC_DEV
default MFD_CROS_EC_DEV
help
If you say Y here, you get support for Type-C PD event notifications
from the ChromeOS EC. On ACPI platorms this driver will bind to the
GOOG0003 ACPI device, and on platforms which don't have this device it
will get initialized on ECs which support the feature
EC_FEATURE_USB_PD.
To compile this driver as a module, choose M here: the
module will be called cros_usbpd_notify.
source "drivers/platform/chrome/wilco_ec/Kconfig"
endif # CHROMEOS_PLATFORMS
......@@ -12,6 +12,7 @@ obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o
obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
obj-$(CONFIG_CROS_EC_TYPEC) += cros_ec_typec.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o
obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
......@@ -19,8 +20,10 @@ obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_chardev.o
obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o
obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o
obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o
obj-$(CONFIG_CROS_EC_SENSORHUB) += cros_ec_sensorhub.o
cros-ec-sensorhub-objs := cros_ec_sensorhub.o cros_ec_sensorhub_ring.o
obj-$(CONFIG_CROS_EC_SENSORHUB) += cros-ec-sensorhub.o
obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o
obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o
obj-$(CONFIG_CROS_USBPD_NOTIFY) += cros_usbpd_notify.o
obj-$(CONFIG_WILCO_EC) += wilco_ec/
......@@ -103,7 +103,7 @@ chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
pr_debug("%d-%02x is probed at %02x\n",
adapter->nr, info->addr, dummy->addr);
i2c_unregister_device(dummy);
client = i2c_new_device(adapter, info);
client = i2c_new_client_device(adapter, info);
}
}
......
......@@ -120,7 +120,7 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
buf.msg.command = EC_CMD_HOST_SLEEP_EVENT;
ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg);
/* For now, report failure to transition to S0ix with a warning. */
if (ret >= 0 && ec_dev->host_sleep_v1 &&
......@@ -138,6 +138,24 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
return ret;
}
static int cros_ec_ready_event(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *_notify)
{
struct cros_ec_device *ec_dev = container_of(nb, struct cros_ec_device,
notifier_ready);
u32 host_event = cros_ec_get_host_event(ec_dev);
if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INTERFACE_READY)) {
mutex_lock(&ec_dev->lock);
cros_ec_query_all(ec_dev);
mutex_unlock(&ec_dev->lock);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
/**
* cros_ec_register() - Register a new ChromeOS EC, using the provided info.
* @ec_dev: Device to register.
......@@ -237,6 +255,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec",
err);
if (ec_dev->mkbp_event_supported) {
/*
* Register the notifier for EC_HOST_EVENT_INTERFACE_READY
* event.
*/
ec_dev->notifier_ready.notifier_call = cros_ec_ready_event;
err = blocking_notifier_chain_register(&ec_dev->event_notifier,
&ec_dev->notifier_ready);
if (err)
return err;
}
dev_info(dev, "Chrome EC device registered\n");
return 0;
......
......@@ -48,7 +48,7 @@ struct ec_event {
struct list_head node;
size_t size;
u8 event_type;
u8 data[0];
u8 data[];
};
static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
......@@ -301,7 +301,7 @@ static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
}
s_cmd->command += ec->cmd_offset;
ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, s_cmd);
/* Only copy data to userland if data was received. */
if (ret < 0)
goto exit;
......
......@@ -116,7 +116,7 @@ static int get_lightbar_version(struct cros_ec_dev *ec,
param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_VERSION;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) {
ret = 0;
goto exit;
......@@ -193,15 +193,10 @@ static ssize_t brightness_store(struct device *dev,
if (ret)
goto exit;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto exit;
}
ret = count;
exit:
kfree(msg);
......@@ -258,13 +253,10 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
goto exit;
}
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto exit;
if (msg->result != EC_RES_SUCCESS)
goto exit;
i = 0;
ok = 1;
}
......@@ -305,14 +297,13 @@ static ssize_t sequence_show(struct device *dev,
if (ret)
goto exit;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret == -EPROTO) {
ret = scnprintf(buf, PAGE_SIZE,
"ERROR: EC returned %d\n", msg->result);
goto exit;
} else if (ret < 0) {
goto exit;
}
resp = (struct ec_response_lightbar *)msg->data;
......@@ -344,13 +335,10 @@ static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
if (ret)
goto error;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto error;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto error;
}
ret = 0;
error:
kfree(msg);
......@@ -377,13 +365,10 @@ static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
if (ret)
goto error;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto error;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto error;
}
ret = 0;
error:
kfree(msg);
......@@ -425,15 +410,10 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
if (ret)
goto exit;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto exit;
}
ret = count;
exit:
kfree(msg);
......@@ -487,13 +467,9 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr,
*/
msg->outsize = count + extra_bytes;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto exit;
}
ret = count;
exit:
......
......@@ -553,7 +553,10 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer);
* replied with success status. It's not necessary to check msg->result when
* using this function.
*
* Return: The number of bytes transferred on success or negative error code.
* Return:
* >=0 - The number of bytes transferred
* -ENOTSUPP - Operation not supported
* -EPROTO - Protocol error
*/
int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg)
......@@ -563,6 +566,10 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
ret = cros_ec_cmd_xfer(ec_dev, msg);
if (ret < 0) {
dev_err(ec_dev->dev, "Command xfer error (err:%d)\n", ret);
} else if (msg->result == EC_RES_INVALID_VERSION) {
dev_dbg(ec_dev->dev, "Command invalid version (err:%d)\n",
msg->result);
return -ENOTSUPP;
} else if (msg->result != EC_RES_SUCCESS) {
dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result);
return -EPROTO;
......
......@@ -44,6 +44,8 @@ struct cros_ec_rpmsg {
struct completion xfer_ack;
struct work_struct host_event_work;
struct rpmsg_endpoint *ept;
bool has_pending_host_event;
bool probe_done;
};
/**
......@@ -177,7 +179,14 @@ static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
memcpy(ec_dev->din, resp->data, len);
complete(&ec_rpmsg->xfer_ack);
} else if (resp->type == HOST_EVENT_MARK) {
schedule_work(&ec_rpmsg->host_event_work);
/*
* If the host event is sent before cros_ec_register is
* finished, queue the host event.
*/
if (ec_rpmsg->probe_done)
schedule_work(&ec_rpmsg->host_event_work);
else
ec_rpmsg->has_pending_host_event = true;
} else {
dev_warn(ec_dev->dev, "rpmsg received invalid type = %d",
resp->type);
......@@ -240,6 +249,11 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
return ret;
}
ec_rpmsg->probe_done = true;
if (ec_rpmsg->has_pending_host_event)
schedule_work(&ec_rpmsg->host_event_work);
return 0;
}
......
......@@ -50,10 +50,8 @@ static int cros_ec_sensorhub_register(struct device *dev,
struct cros_ec_sensorhub *sensorhub)
{
int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
struct cros_ec_command *msg = sensorhub->msg;
struct cros_ec_dev *ec = sensorhub->ec;
struct ec_params_motion_sense *params;
struct ec_response_motion_sense *resp;
struct cros_ec_command *msg;
int ret, i, sensor_num;
char *name;
......@@ -65,27 +63,19 @@ static int cros_ec_sensorhub_register(struct device *dev,
return sensor_num;
}
sensorhub->sensor_num = sensor_num;
if (sensor_num == 0) {
dev_err(dev, "Zero sensors reported.\n");
return -EINVAL;
}
/* Prepare a message to send INFO command to each sensor. */
msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)),
GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->version = 1;
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;
resp = (struct ec_response_motion_sense *)msg->data;
msg->insize = sizeof(struct ec_response_motion_sense);
msg->outsize = sizeof(struct ec_params_motion_sense);
for (i = 0; i < sensor_num; i++) {
params->cmd = MOTIONSENSE_CMD_INFO;
params->info.sensor_num = i;
sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
sensorhub->params->info.sensor_num = i;
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) {
......@@ -94,7 +84,7 @@ static int cros_ec_sensorhub_register(struct device *dev,
continue;
}
switch (resp->info.type) {
switch (sensorhub->resp->info.type) {
case MOTIONSENSE_TYPE_ACCEL:
name = "cros-ec-accel";
break;
......@@ -117,15 +107,16 @@ static int cros_ec_sensorhub_register(struct device *dev,
name = "cros-ec-activity";
break;
default:
dev_warn(dev, "unknown type %d\n", resp->info.type);
dev_warn(dev, "unknown type %d\n",
sensorhub->resp->info.type);
continue;
}
ret = cros_ec_sensorhub_allocate_sensor(dev, name, i);
if (ret)
goto error;
return ret;
sensor_type[resp->info.type]++;
sensor_type[sensorhub->resp->info.type]++;
}
if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
......@@ -137,29 +128,41 @@ static int cros_ec_sensorhub_register(struct device *dev,
"cros-ec-lid-angle",
0);
if (ret)
goto error;
return ret;
}
kfree(msg);
return 0;
error:
kfree(msg);
return ret;
}
static int cros_ec_sensorhub_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
struct cros_ec_sensorhub *data;
struct cros_ec_command *msg;
int ret;
int i;
msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) +
max((u16)sizeof(struct ec_params_motion_sense),
ec->ec_dev->max_response), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->ec = dev_get_drvdata(dev->parent);
mutex_init(&data->cmd_lock);
data->dev = dev;
data->ec = ec;
data->msg = msg;
data->params = (struct ec_params_motion_sense *)msg->data;
data->resp = (struct ec_response_motion_sense *)msg->data;
dev_set_drvdata(dev, data);
/* Check whether this EC is a sensor hub. */
......@@ -172,7 +175,8 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
* If the device has sensors but does not claim to
* be a sensor hub, we are in legacy mode.
*/
for (i = 0; i < 2; i++) {
data->sensor_num = 2;
for (i = 0; i < data->sensor_num; i++) {
ret = cros_ec_sensorhub_allocate_sensor(dev,
"cros-ec-accel-legacy", i);
if (ret)
......@@ -180,12 +184,63 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
}
}
/*
* If the EC does not have a FIFO, the sensors will query their data
* themselves via sysfs or a software trigger.
*/
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
ret = cros_ec_sensorhub_ring_add(data);
if (ret)
return ret;
/*
* The msg and its data is not under the control of the ring
* handler.
*/
return devm_add_action_or_reset(dev,
cros_ec_sensorhub_ring_remove,
data);
}
return 0;
}
#ifdef CONFIG_PM_SLEEP
/*
* When the EC is suspending, we must stop sending interrupt,
* we may use the same interrupt line for waking up the device.
* Tell the EC to stop sending non-interrupt event on the iio ring.
*/
static int cros_ec_sensorhub_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
struct cros_ec_dev *ec = sensorhub->ec;
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
return cros_ec_sensorhub_ring_fifo_enable(sensorhub, false);
return 0;
}
static int cros_ec_sensorhub_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev);
struct cros_ec_dev *ec = sensorhub->ec;
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_pm_ops,
cros_ec_sensorhub_suspend,
cros_ec_sensorhub_resume);
static struct platform_driver cros_ec_sensorhub_driver = {
.driver = {
.name = DRV_NAME,
.pm = &cros_ec_sensorhub_pm_ops,
},
.probe = cros_ec_sensorhub_probe,
};
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Chrome OS EC Sensor hub FIFO.
*
* Copyright 2020 Google LLC
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.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>
#include <linux/sort.h>
#include <linux/slab.h>
/* Precision of fixed point for the m values from the filter */
#define M_PRECISION BIT(23)
/* Only activate the filter once we have at least this many elements. */
#define TS_HISTORY_THRESHOLD 8
/*
* If we don't have any history entries for this long, empty the filter to
* make sure there are no big discontinuities.
*/
#define TS_HISTORY_BORED_US 500000
/* To measure by how much the filter is overshooting, if it happens. */
#define FUTURE_TS_ANALYTICS_COUNT_MAX 100
static inline int
cros_sensorhub_send_sample(struct cros_ec_sensorhub *sensorhub,
struct cros_ec_sensors_ring_sample *sample)
{
cros_ec_sensorhub_push_data_cb_t cb;
int id = sample->sensor_id;
struct iio_dev *indio_dev;
if (id > sensorhub->sensor_num)
return -EINVAL;
cb = sensorhub->push_data[id].push_data_cb;
if (!cb)
return 0;
indio_dev = sensorhub->push_data[id].indio_dev;
if (sample->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
return 0;
return cb(indio_dev, sample->vector, sample->timestamp);
}
/**
* cros_ec_sensorhub_register_push_data() - register the callback to the hub.
*
* @sensorhub : Sensor Hub object
* @sensor_num : The sensor the caller is interested in.
* @indio_dev : The iio device to use when a sample arrives.
* @cb : The callback to call when a sample arrives.
*
* The callback cb will be used by cros_ec_sensorhub_ring to distribute events
* from the EC.
*
* Return: 0 when callback is registered.
* EINVAL is the sensor number is invalid or the slot already used.
*/
int cros_ec_sensorhub_register_push_data(struct cros_ec_sensorhub *sensorhub,
u8 sensor_num,
struct iio_dev *indio_dev,
cros_ec_sensorhub_push_data_cb_t cb)
{
if (sensor_num >= sensorhub->sensor_num)
return -EINVAL;
if (sensorhub->push_data[sensor_num].indio_dev)
return -EINVAL;
sensorhub->push_data[sensor_num].indio_dev = indio_dev;
sensorhub->push_data[sensor_num].push_data_cb = cb;
return 0;
}
EXPORT_SYMBOL_GPL(cros_ec_sensorhub_register_push_data);
void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
u8 sensor_num)
{
sensorhub->push_data[sensor_num].indio_dev = NULL;
sensorhub->push_data[sensor_num].push_data_cb = NULL;
}
EXPORT_SYMBOL_GPL(cros_ec_sensorhub_unregister_push_data);
/**
* cros_ec_sensorhub_ring_fifo_enable() - Enable or disable interrupt generation
* for FIFO events.
* @sensorhub: Sensor Hub object
* @on: true when events are requested.
*
* To be called before sleeping or when noone is listening.
* Return: 0 on success, or an error when we can not communicate with the EC.
*
*/
int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
bool on)
{
int ret, i;
mutex_lock(&sensorhub->cmd_lock);
if (sensorhub->tight_timestamps)
for (i = 0; i < sensorhub->sensor_num; i++)
sensorhub->batch_state[i].last_len = 0;
sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE;
sensorhub->params->fifo_int_enable.enable = on;
sensorhub->msg->outsize = sizeof(struct ec_params_motion_sense);
sensorhub->msg->insize = sizeof(struct ec_response_motion_sense);
ret = cros_ec_cmd_xfer_status(sensorhub->ec->ec_dev, sensorhub->msg);
mutex_unlock(&sensorhub->cmd_lock);
/* We expect to receive a payload of 4 bytes, ignore. */
if (ret > 0)
ret = 0;
return ret;
}
static int cros_ec_sensor_ring_median_cmp(const void *pv1, const void *pv2)
{
s64 v1 = *(s64 *)pv1;
s64 v2 = *(s64 *)pv2;
if (v1 > v2)
return 1;
else if (v1 < v2)
return -1;
else
return 0;
}
/*
* cros_ec_sensor_ring_median: Gets median of an array of numbers
*
* For now it's implemented using an inefficient > O(n) sort then return
* the middle element. A more optimal method would be something like
* quickselect, but given that n = 64 we can probably live with it in the
* name of clarity.
*
* Warning: the input array gets modified (sorted)!
*/
static s64 cros_ec_sensor_ring_median(s64 *array, size_t length)
{
sort(array, length, sizeof(s64), cros_ec_sensor_ring_median_cmp, NULL);
return array[length / 2];
}
/*
* IRQ Timestamp Filtering
*
* Lower down in cros_ec_sensor_ring_process_event(), for each sensor event
* we have to calculate it's timestamp in the AP timebase. There are 3 time
* points:
* a - EC timebase, sensor event
* b - EC timebase, IRQ
* c - AP timebase, IRQ
* a' - what we want: sensor even in AP timebase
*
* While a and b are recorded at accurate times (due to the EC real time
* nature); c is pretty untrustworthy, even though it's recorded the
* first thing in ec_irq_handler(). There is a very good change we'll get
* added lantency due to:
* other irqs
* ddrfreq
* cpuidle
*
* Normally a' = c - b + a, but if we do that naive math any jitter in c
* will get coupled in a', which we don't want. We want a function
* a' = cros_ec_sensor_ring_ts_filter(a) which will filter out outliers in c.
*
* Think of a graph of AP time(b) on the y axis vs EC time(c) on the x axis.
* The slope of the line won't be exactly 1, there will be some clock drift
* between the 2 chips for various reasons (mechanical stress, temperature,
* voltage). We need to extrapolate values for a future x, without trusting
* recent y values too much.
*
* We use a median filter for the slope, then another median filter for the
* y-intercept to calculate this function:
* dx[n] = x[n-1] - x[n]
* dy[n] = x[n-1] - x[n]
* m[n] = dy[n] / dx[n]
* median_m = median(m[n-k:n])
* error[i] = y[n-i] - median_m * x[n-i]
* median_error = median(error[:k])
* predicted_y = median_m * x + median_error
*
* Implementation differences from above:
* - Redefined y to be actually c - b, this gives us a lot more precision
* to do the math. (c-b)/b variations are more obvious than c/b variations.
* - Since we don't have floating point, any operations involving slope are
* done using fixed point math (*M_PRECISION)
* - Since x and y grow with time, we keep zeroing the graph (relative to
* the last sample), this way math involving *x[n-i] will not overflow
* - EC timestamps are kept in us, it improves the slope calculation precision
*/
/**
* cros_ec_sensor_ring_ts_filter_update() - Update filter history.
*
* @state: Filter information.
* @b: IRQ timestamp, EC timebase (us)
* @c: IRQ timestamp, AP timebase (ns)
*
* Given a new IRQ timestamp pair (EC and AP timebases), add it to the filter
* history.
*/
static void
cros_ec_sensor_ring_ts_filter_update(struct cros_ec_sensors_ts_filter_state
*state,
s64 b, s64 c)
{
s64 x, y;
s64 dx, dy;
s64 m; /* stored as *M_PRECISION */
s64 *m_history_copy = state->temp_buf;
s64 *error = state->temp_buf;
int i;
/* we trust b the most, that'll be our independent variable */
x = b;
/* y is the offset between AP and EC times, in ns */
y = c - b * 1000;
dx = (state->x_history[0] + state->x_offset) - x;
if (dx == 0)
return; /* we already have this irq in the history */
dy = (state->y_history[0] + state->y_offset) - y;
m = div64_s64(dy * M_PRECISION, dx);
/* Empty filter if we haven't seen any action in a while. */
if (-dx > TS_HISTORY_BORED_US)
state->history_len = 0;
/* Move everything over, also update offset to all absolute coords .*/
for (i = state->history_len - 1; i >= 1; i--) {
state->x_history[i] = state->x_history[i - 1] + dx;
state->y_history[i] = state->y_history[i - 1] + dy;
state->m_history[i] = state->m_history[i - 1];
/*
* Also use the same loop to copy m_history for future
* median extraction.
*/
m_history_copy[i] = state->m_history[i - 1];
}
/* Store the x and y, but remember offset is actually last sample. */
state->x_offset = x;
state->y_offset = y;
state->x_history[0] = 0;
state->y_history[0] = 0;
state->m_history[0] = m;
m_history_copy[0] = m;
if (state->history_len < CROS_EC_SENSORHUB_TS_HISTORY_SIZE)
state->history_len++;
/* Precalculate things for the filter. */
if (state->history_len > TS_HISTORY_THRESHOLD) {
state->median_m =
cros_ec_sensor_ring_median(m_history_copy,
state->history_len - 1);
/*
* Calculate y-intercepts as if m_median is the slope and
* points in the history are on the line. median_error will
* still be in the offset coordinate system.
*/
for (i = 0; i < state->history_len; i++)
error[i] = state->y_history[i] -
div_s64(state->median_m * state->x_history[i],
M_PRECISION);
state->median_error =
cros_ec_sensor_ring_median(error, state->history_len);
} else {
state->median_m = 0;
state->median_error = 0;
}
}
/**
* cros_ec_sensor_ring_ts_filter() - Translate EC timebase timestamp to AP
* timebase
*
* @state: filter information.
* @x: any ec timestamp (us):
*
* cros_ec_sensor_ring_ts_filter(a) => a' event timestamp, AP timebase
* cros_ec_sensor_ring_ts_filter(b) => calculated timestamp when the EC IRQ
* should have happened on the AP, with low jitter
*
* Note: The filter will only activate once state->history_len goes
* over TS_HISTORY_THRESHOLD. Otherwise it'll just do the naive c - b + a
* transform.
*
* How to derive the formula, starting from:
* f(x) = median_m * x + median_error
* That's the calculated AP - EC offset (at the x point in time)
* Undo the coordinate system transform:
* f(x) = median_m * (x - x_offset) + median_error + y_offset
* Remember to undo the "y = c - b * 1000" modification:
* f(x) = median_m * (x - x_offset) + median_error + y_offset + x * 1000
*
* Return: timestamp in AP timebase (ns)
*/
static s64
cros_ec_sensor_ring_ts_filter(struct cros_ec_sensors_ts_filter_state *state,
s64 x)
{
return div_s64(state->median_m * (x - state->x_offset), M_PRECISION)
+ state->median_error + state->y_offset + x * 1000;
}
/*
* Since a and b were originally 32 bit values from the EC,
* they overflow relatively often, casting is not enough, so we need to
* add an offset.
*/
static void
cros_ec_sensor_ring_fix_overflow(s64 *ts,
const s64 overflow_period,
struct cros_ec_sensors_ec_overflow_state
*state)
{
s64 adjust;
*ts += state->offset;
if (abs(state->last - *ts) > (overflow_period / 2)) {
adjust = state->last > *ts ? overflow_period : -overflow_period;
state->offset += adjust;
*ts += adjust;
}
state->last = *ts;
}
static void
cros_ec_sensor_ring_check_for_past_timestamp(struct cros_ec_sensorhub
*sensorhub,
struct cros_ec_sensors_ring_sample
*sample)
{
const u8 sensor_id = sample->sensor_id;
/* If this event is earlier than one we saw before... */
if (sensorhub->batch_state[sensor_id].newest_sensor_event >
sample->timestamp)
/* mark it for spreading. */
sample->timestamp =
sensorhub->batch_state[sensor_id].last_ts;
else
sensorhub->batch_state[sensor_id].newest_sensor_event =
sample->timestamp;
}
/**
* cros_ec_sensor_ring_process_event() - Process one EC FIFO event
*
* @sensorhub: Sensor Hub object.
* @fifo_info: FIFO information from the EC (includes b point, EC timebase).
* @fifo_timestamp: EC IRQ, kernel timebase (aka c).
* @current_timestamp: calculated event timestamp, kernel timebase (aka a').
* @in: incoming FIFO event from EC (includes a point, EC timebase).
* @out: outgoing event to user space (includes a').
*
* Process one EC event, add it in the ring if necessary.
*
* Return: true if out event has been populated.
*/
static bool
cros_ec_sensor_ring_process_event(struct cros_ec_sensorhub *sensorhub,
const struct ec_response_motion_sense_fifo_info
*fifo_info,
const ktime_t fifo_timestamp,
ktime_t *current_timestamp,
struct ec_response_motion_sensor_data *in,
struct cros_ec_sensors_ring_sample *out)
{
const s64 now = cros_ec_get_time_ns();
int axis, async_flags;
/* Do not populate the filter based on asynchronous events. */
async_flags = in->flags &
(MOTIONSENSE_SENSOR_FLAG_ODR | MOTIONSENSE_SENSOR_FLAG_FLUSH);
if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP && !async_flags) {
s64 a = in->timestamp;
s64 b = fifo_info->timestamp;
s64 c = fifo_timestamp;
cros_ec_sensor_ring_fix_overflow(&a, 1LL << 32,
&sensorhub->overflow_a);
cros_ec_sensor_ring_fix_overflow(&b, 1LL << 32,
&sensorhub->overflow_b);
if (sensorhub->tight_timestamps) {
cros_ec_sensor_ring_ts_filter_update(
&sensorhub->filter, b, c);
*current_timestamp = cros_ec_sensor_ring_ts_filter(
&sensorhub->filter, a);
} else {
s64 new_timestamp;
/*
* Disable filtering since we might add more jitter
* if b is in a random point in time.
*/
new_timestamp = fifo_timestamp -
fifo_info->timestamp * 1000 +
in->timestamp * 1000;
/*
* The timestamp can be stale if we had to use the fifo
* info timestamp.
*/
if (new_timestamp - *current_timestamp > 0)
*current_timestamp = new_timestamp;
}
}
if (in->flags & MOTIONSENSE_SENSOR_FLAG_ODR) {
if (sensorhub->tight_timestamps) {
sensorhub->batch_state[in->sensor_num].last_len = 0;
sensorhub->batch_state[in->sensor_num].penul_len = 0;
}
/*
* ODR change is only useful for the sensor_ring, it does not
* convey information to clients.
*/
return false;
}
if (in->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
out->sensor_id = in->sensor_num;
out->timestamp = *current_timestamp;
out->flag = in->flags;
if (sensorhub->tight_timestamps)
sensorhub->batch_state[out->sensor_id].last_len = 0;
/*
* No other payload information provided with
* flush ack.
*/
return true;
}
if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
/* If we just have a timestamp, skip this entry. */
return false;
/* Regular sample */
out->sensor_id = in->sensor_num;
if (*current_timestamp - now > 0) {
/*
* This fix is needed to overcome the timestamp filter putting
* events in the future.
*/
sensorhub->future_timestamp_total_ns +=
*current_timestamp - now;
if (++sensorhub->future_timestamp_count ==
FUTURE_TS_ANALYTICS_COUNT_MAX) {
s64 avg = div_s64(sensorhub->future_timestamp_total_ns,
sensorhub->future_timestamp_count);
dev_warn_ratelimited(sensorhub->dev,
"100 timestamps in the future, %lldns shaved on average\n",
avg);
sensorhub->future_timestamp_count = 0;
sensorhub->future_timestamp_total_ns = 0;
}
out->timestamp = now;
} else {
out->timestamp = *current_timestamp;
}
out->flag = in->flags;
for (axis = 0; axis < 3; axis++)
out->vector[axis] = in->data[axis];
if (sensorhub->tight_timestamps)
cros_ec_sensor_ring_check_for_past_timestamp(sensorhub, out);
return true;
}
/*
* cros_ec_sensor_ring_spread_add: Calculate proper timestamps then add to
* ringbuffer.
*
* This is the new spreading code, assumes every sample's timestamp
* preceeds the sample. Run if tight_timestamps == true.
*
* Sometimes the EC receives only one interrupt (hence timestamp) for
* a batch of samples. Only the first sample will have the correct
* timestamp. So we must interpolate the other samples.
* We use the previous batch timestamp and our current batch timestamp
* as a way to calculate period, then spread the samples evenly.
*
* s0 int, 0ms
* s1 int, 10ms
* s2 int, 20ms
* 30ms point goes by, no interrupt, previous one is still asserted
* downloading s2 and s3
* s3 sample, 20ms (incorrect timestamp)
* s4 int, 40ms
*
* The batches are [(s0), (s1), (s2, s3), (s4)]. Since the 3rd batch
* has 2 samples in them, we adjust the timestamp of s3.
* s2 - s1 = 10ms, so s3 must be s2 + 10ms => 20ms. If s1 would have
* been part of a bigger batch things would have gotten a little
* more complicated.
*
* Note: we also assume another sensor sample doesn't break up a batch
* in 2 or more partitions. Example, there can't ever be a sync sensor
* in between S2 and S3. This simplifies the following code.
*/
static void
cros_ec_sensor_ring_spread_add(struct cros_ec_sensorhub *sensorhub,
unsigned long sensor_mask,
struct cros_ec_sensors_ring_sample *last_out)
{
struct cros_ec_sensors_ring_sample *batch_start, *next_batch_start;
int id;
for_each_set_bit(id, &sensor_mask, sensorhub->sensor_num) {
for (batch_start = sensorhub->ring; batch_start < last_out;
batch_start = next_batch_start) {
/*
* For each batch (where all samples have the same
* timestamp).
*/
int batch_len, sample_idx;
struct cros_ec_sensors_ring_sample *batch_end =
batch_start;
struct cros_ec_sensors_ring_sample *s;
s64 batch_timestamp = batch_start->timestamp;
s64 sample_period;
/*
* Skip over batches that start with the sensor types
* we're not looking at right now.
*/
if (batch_start->sensor_id != id) {
next_batch_start = batch_start + 1;
continue;
}
/*
* Do not start a batch
* from a flush, as it happens asynchronously to the
* regular flow of events.
*/
if (batch_start->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH) {
cros_sensorhub_send_sample(sensorhub,
batch_start);
next_batch_start = batch_start + 1;
continue;
}
if (batch_start->timestamp <=
sensorhub->batch_state[id].last_ts) {
batch_timestamp =
sensorhub->batch_state[id].last_ts;
batch_len = sensorhub->batch_state[id].last_len;
sample_idx = batch_len;
sensorhub->batch_state[id].last_ts =
sensorhub->batch_state[id].penul_ts;
sensorhub->batch_state[id].last_len =
sensorhub->batch_state[id].penul_len;
} else {
/*
* Push first sample in the batch to the,
* kifo, it's guaranteed to be correct, the
* rest will follow later on.
*/
sample_idx = 1;
batch_len = 1;
cros_sensorhub_send_sample(sensorhub,
batch_start);
batch_start++;
}
/* Find all samples have the same timestamp. */
for (s = batch_start; s < last_out; s++) {
if (s->sensor_id != id)
/*
* Skip over other sensor types that
* are interleaved, don't count them.
*/
continue;
if (s->timestamp != batch_timestamp)
/* we discovered the next batch */
break;
if (s->flag & MOTIONSENSE_SENSOR_FLAG_FLUSH)
/* break on flush packets */
break;
batch_end = s;
batch_len++;
}
if (batch_len == 1)
goto done_with_this_batch;
/* Can we calculate period? */
if (sensorhub->batch_state[id].last_len == 0) {
dev_warn(sensorhub->dev, "Sensor %d: lost %d samples when spreading\n",
id, batch_len - 1);
goto done_with_this_batch;
/*
* Note: we're dropping the rest of the samples
* in this batch since we have no idea where
* they're supposed to go without a period
* calculation.
*/
}
sample_period = div_s64(batch_timestamp -
sensorhub->batch_state[id].last_ts,
sensorhub->batch_state[id].last_len);
dev_dbg(sensorhub->dev,
"Adjusting %d samples, sensor %d last_batch @%lld (%d samples) batch_timestamp=%lld => period=%lld\n",
batch_len, id,
sensorhub->batch_state[id].last_ts,
sensorhub->batch_state[id].last_len,
batch_timestamp,
sample_period);
/*
* Adjust timestamps of the samples then push them to
* kfifo.
*/
for (s = batch_start; s <= batch_end; s++) {
if (s->sensor_id != id)
/*
* Skip over other sensor types that
* are interleaved, don't change them.
*/
continue;
s->timestamp = batch_timestamp +
sample_period * sample_idx;
sample_idx++;
cros_sensorhub_send_sample(sensorhub, s);
}
done_with_this_batch:
sensorhub->batch_state[id].penul_ts =
sensorhub->batch_state[id].last_ts;
sensorhub->batch_state[id].penul_len =
sensorhub->batch_state[id].last_len;
sensorhub->batch_state[id].last_ts =
batch_timestamp;
sensorhub->batch_state[id].last_len = batch_len;
next_batch_start = batch_end + 1;
}
}
}
/*
* cros_ec_sensor_ring_spread_add_legacy: Calculate proper timestamps then
* add to ringbuffer (legacy).
*
* Note: This assumes we're running old firmware, where every sample's timestamp
* is after the sample. Run if tight_timestamps == false.
*
* If there is a sample with a proper timestamp
*
* timestamp | count
* -----------------
* older_unprocess_out --> TS1 | 1
* TS1 | 2
* out --> TS1 | 3
* next_out --> TS2 |
*
* We spread time for the samples [older_unprocess_out .. out]
* between TS1 and TS2: [TS1+1/4, TS1+2/4, TS1+3/4, TS2].
*
* If we reach the end of the samples, we compare with the
* current timestamp:
*
* older_unprocess_out --> TS1 | 1
* TS1 | 2
* out --> TS1 | 3
*
* We know have [TS1+1/3, TS1+2/3, current timestamp]
*/
static void
cros_ec_sensor_ring_spread_add_legacy(struct cros_ec_sensorhub *sensorhub,
unsigned long sensor_mask,
s64 current_timestamp,
struct cros_ec_sensors_ring_sample
*last_out)
{
struct cros_ec_sensors_ring_sample *out;
int i;
for_each_set_bit(i, &sensor_mask, sensorhub->sensor_num) {
s64 older_timestamp;
s64 timestamp;
struct cros_ec_sensors_ring_sample *older_unprocess_out =
sensorhub->ring;
struct cros_ec_sensors_ring_sample *next_out;
int count = 1;
for (out = sensorhub->ring; out < last_out; out = next_out) {
s64 time_period;
next_out = out + 1;
if (out->sensor_id != i)
continue;
/* Timestamp to start with */
older_timestamp = out->timestamp;
/* Find next sample. */
while (next_out < last_out && next_out->sensor_id != i)
next_out++;
if (next_out >= last_out) {
timestamp = current_timestamp;
} else {
timestamp = next_out->timestamp;
if (timestamp == older_timestamp) {
count++;
continue;
}
}
/*
* The next sample has a new timestamp, spread the
* unprocessed samples.
*/
if (next_out < last_out)
count++;
time_period = div_s64(timestamp - older_timestamp,
count);
for (; older_unprocess_out <= out;
older_unprocess_out++) {
if (older_unprocess_out->sensor_id != i)
continue;
older_timestamp += time_period;
older_unprocess_out->timestamp =
older_timestamp;
}
count = 1;
/* The next_out sample has a valid timestamp, skip. */
next_out++;
older_unprocess_out = next_out;
}
}
/* Push the event into the kfifo */
for (out = sensorhub->ring; out < last_out; out++)
cros_sensorhub_send_sample(sensorhub, out);
}
/**
* cros_ec_sensorhub_ring_handler() - The trigger handler function
*
* @sensorhub: Sensor Hub object.
*
* Called by the notifier, process the EC sensor FIFO queue.
*/
static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
{
struct ec_response_motion_sense_fifo_info *fifo_info =
sensorhub->fifo_info;
struct cros_ec_dev *ec = sensorhub->ec;
ktime_t fifo_timestamp, current_timestamp;
int i, j, number_data, ret;
unsigned long sensor_mask = 0;
struct ec_response_motion_sensor_data *in;
struct cros_ec_sensors_ring_sample *out, *last_out;
mutex_lock(&sensorhub->cmd_lock);
/* Get FIFO information if there are lost vectors. */
if (fifo_info->total_lost) {
int fifo_info_length =
sizeof(struct ec_response_motion_sense_fifo_info) +
sizeof(u16) * sensorhub->sensor_num;
/* Need to retrieve the number of lost vectors per sensor */
sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
sensorhub->msg->outsize = 1;
sensorhub->msg->insize = fifo_info_length;
if (cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg) < 0)
goto error;
memcpy(fifo_info, &sensorhub->resp->fifo_info,
fifo_info_length);
/*
* Update collection time, will not be as precise as the
* non-error case.
*/
fifo_timestamp = cros_ec_get_time_ns();
} else {
fifo_timestamp = sensorhub->fifo_timestamp[
CROS_EC_SENSOR_NEW_TS];
}
if (fifo_info->count > sensorhub->fifo_size ||
fifo_info->size != sensorhub->fifo_size) {
dev_warn(sensorhub->dev,
"Mismatch EC data: count %d, size %d - expected %d",
fifo_info->count, fifo_info->size,
sensorhub->fifo_size);
goto error;
}
/* Copy elements in the main fifo */
current_timestamp = sensorhub->fifo_timestamp[CROS_EC_SENSOR_LAST_TS];
out = sensorhub->ring;
for (i = 0; i < fifo_info->count; i += number_data) {
sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_READ;
sensorhub->params->fifo_read.max_data_vector =
fifo_info->count - i;
sensorhub->msg->outsize =
sizeof(struct ec_params_motion_sense);
sensorhub->msg->insize =
sizeof(sensorhub->resp->fifo_read) +
sensorhub->params->fifo_read.max_data_vector *
sizeof(struct ec_response_motion_sensor_data);
ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
if (ret < 0) {
dev_warn(sensorhub->dev, "Fifo error: %d\n", ret);
break;
}
number_data = sensorhub->resp->fifo_read.number_data;
if (number_data == 0) {
dev_dbg(sensorhub->dev, "Unexpected empty FIFO\n");
break;
}
if (number_data > fifo_info->count - i) {
dev_warn(sensorhub->dev,
"Invalid EC data: too many entry received: %d, expected %d",
number_data, fifo_info->count - i);
break;
}
if (out + number_data >
sensorhub->ring + fifo_info->count) {
dev_warn(sensorhub->dev,
"Too many samples: %d (%zd data) to %d entries for expected %d entries",
i, out - sensorhub->ring, i + number_data,
fifo_info->count);
break;
}
for (in = sensorhub->resp->fifo_read.data, j = 0;
j < number_data; j++, in++) {
if (cros_ec_sensor_ring_process_event(
sensorhub, fifo_info,
fifo_timestamp,
&current_timestamp,
in, out)) {
sensor_mask |= BIT(in->sensor_num);
out++;
}
}
}
mutex_unlock(&sensorhub->cmd_lock);
last_out = out;
if (out == sensorhub->ring)
/* Unexpected empty FIFO. */
goto ring_handler_end;
/*
* Check if current_timestamp is ahead of the last sample. Normally,
* the EC appends a timestamp after the last sample, but if the AP
* is slow to respond to the IRQ, the EC may have added new samples.
* Use the FIFO info timestamp as last timestamp then.
*/
if (!sensorhub->tight_timestamps &&
(last_out - 1)->timestamp == current_timestamp)
current_timestamp = fifo_timestamp;
/* Warn on lost samples. */
if (fifo_info->total_lost)
for (i = 0; i < sensorhub->sensor_num; i++) {
if (fifo_info->lost[i]) {
dev_warn_ratelimited(sensorhub->dev,
"Sensor %d: lost: %d out of %d\n",
i, fifo_info->lost[i],
fifo_info->total_lost);
if (sensorhub->tight_timestamps)
sensorhub->batch_state[i].last_len = 0;
}
}
/*
* Spread samples in case of batching, then add them to the
* ringbuffer.
*/
if (sensorhub->tight_timestamps)
cros_ec_sensor_ring_spread_add(sensorhub, sensor_mask,
last_out);
else
cros_ec_sensor_ring_spread_add_legacy(sensorhub, sensor_mask,
current_timestamp,
last_out);
ring_handler_end:
sensorhub->fifo_timestamp[CROS_EC_SENSOR_LAST_TS] = current_timestamp;
return;
error:
mutex_unlock(&sensorhub->cmd_lock);
}
static int cros_ec_sensorhub_event(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *_notify)
{
struct cros_ec_sensorhub *sensorhub;
struct cros_ec_device *ec_dev;
sensorhub = container_of(nb, struct cros_ec_sensorhub, notifier);
ec_dev = sensorhub->ec->ec_dev;
if (ec_dev->event_data.event_type != EC_MKBP_EVENT_SENSOR_FIFO)
return NOTIFY_DONE;
if (ec_dev->event_size != sizeof(ec_dev->event_data.data.sensor_fifo)) {
dev_warn(ec_dev->dev, "Invalid fifo info size\n");
return NOTIFY_DONE;
}
if (queued_during_suspend)
return NOTIFY_OK;
memcpy(sensorhub->fifo_info, &ec_dev->event_data.data.sensor_fifo.info,
sizeof(*sensorhub->fifo_info));
sensorhub->fifo_timestamp[CROS_EC_SENSOR_NEW_TS] =
ec_dev->last_event_time;
cros_ec_sensorhub_ring_handler(sensorhub);
return NOTIFY_OK;
}
/**
* cros_ec_sensorhub_ring_add() - Add the FIFO functionality if the EC
* supports it.
*
* @sensorhub : Sensor Hub object.
*
* Return: 0 on success.
*/
int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
{
struct cros_ec_dev *ec = sensorhub->ec;
int ret;
int fifo_info_length =
sizeof(struct ec_response_motion_sense_fifo_info) +
sizeof(u16) * sensorhub->sensor_num;
/* Allocate the array for lost events. */
sensorhub->fifo_info = devm_kzalloc(sensorhub->dev, fifo_info_length,
GFP_KERNEL);
if (!sensorhub->fifo_info)
return -ENOMEM;
/* Retrieve FIFO information */
sensorhub->msg->version = 2;
sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
sensorhub->msg->outsize = 1;
sensorhub->msg->insize = fifo_info_length;
ret = cros_ec_cmd_xfer_status(ec->ec_dev, sensorhub->msg);
if (ret < 0)
return ret;
/*
* Allocate the full fifo. We need to copy the whole FIFO to set
* timestamps properly.
*/
sensorhub->fifo_size = sensorhub->resp->fifo_info.size;
sensorhub->ring = devm_kcalloc(sensorhub->dev, sensorhub->fifo_size,
sizeof(*sensorhub->ring), GFP_KERNEL);
if (!sensorhub->ring)
return -ENOMEM;
/*
* Allocate the callback area based on the number of sensors.
*/
sensorhub->push_data = devm_kcalloc(
sensorhub->dev, sensorhub->sensor_num,
sizeof(*sensorhub->push_data),
GFP_KERNEL);
if (!sensorhub->push_data)
return -ENOMEM;
sensorhub->fifo_timestamp[CROS_EC_SENSOR_LAST_TS] =
cros_ec_get_time_ns();
sensorhub->tight_timestamps = cros_ec_check_features(
ec, EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS);
if (sensorhub->tight_timestamps) {
sensorhub->batch_state = devm_kcalloc(sensorhub->dev,
sensorhub->sensor_num,
sizeof(*sensorhub->batch_state),
GFP_KERNEL);
if (!sensorhub->batch_state)
return -ENOMEM;
}
/* Register the notifier that will act as a top half interrupt. */
sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
&sensorhub->notifier);
if (ret < 0)
return ret;
/* Start collection samples. */
return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);
}
void cros_ec_sensorhub_ring_remove(void *arg)
{
struct cros_ec_sensorhub *sensorhub = arg;
struct cros_ec_device *ec_dev = sensorhub->ec->ec_dev;
/* Disable the ring, prevent EC interrupt to the AP for nothing. */
cros_ec_sensorhub_ring_fifo_enable(sensorhub, false);
blocking_notifier_chain_unregister(&ec_dev->event_notifier,
&sensorhub->notifier);
}
......@@ -127,7 +127,8 @@ static int terminate_request(struct cros_ec_device *ec_dev)
*/
spi_message_init(&msg);
memset(&trans, 0, sizeof(trans));
trans.delay_usecs = ec_spi->end_of_msg_delay;
trans.delay.value = ec_spi->end_of_msg_delay;
trans.delay.unit = SPI_DELAY_UNIT_USECS;
spi_message_add_tail(&trans, &msg);
ret = spi_sync_locked(ec_spi->spi, &msg);
......@@ -416,7 +417,8 @@ static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
spi_message_init(&msg);
if (ec_spi->start_of_msg_delay) {
memset(&trans_delay, 0, sizeof(trans_delay));
trans_delay.delay_usecs = ec_spi->start_of_msg_delay;
trans_delay.delay.value = ec_spi->start_of_msg_delay;
trans_delay.delay.unit = SPI_DELAY_UNIT_USECS;
spi_message_add_tail(&trans_delay, &msg);
}
......
......@@ -149,14 +149,14 @@ static ssize_t version_show(struct device *dev,
/* Get build info. */
msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
msg->insize = EC_HOST_PARAM_SIZE;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: XFER ERROR %d\n", ret);
else if (msg->result != EC_RES_SUCCESS)
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret == -EPROTO) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: EC error %d\n", msg->result);
else {
} else if (ret < 0) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: XFER ERROR %d\n", ret);
} else {
msg->data[EC_HOST_PARAM_SIZE - 1] = '\0';
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: %s\n", msg->data);
......@@ -165,14 +165,14 @@ static ssize_t version_show(struct device *dev,
/* Get chip info. */
msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
msg->insize = sizeof(*r_chip);
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: XFER ERROR %d\n", ret);
else if (msg->result != EC_RES_SUCCESS)
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret == -EPROTO) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: EC error %d\n", msg->result);
else {
} else if (ret < 0) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: XFER ERROR %d\n", ret);
} else {
r_chip = (struct ec_response_get_chip_info *)msg->data;
r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
......@@ -189,14 +189,14 @@ static ssize_t version_show(struct device *dev,
/* Get board version */
msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
msg->insize = sizeof(*r_board);
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: XFER ERROR %d\n", ret);
else if (msg->result != EC_RES_SUCCESS)
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret == -EPROTO) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: EC error %d\n", msg->result);
else {
} else if (ret < 0) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: XFER ERROR %d\n", ret);
} else {
r_board = (struct ec_response_board_version *)msg->data;
count += scnprintf(buf + count, PAGE_SIZE - count,
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2020 Google LLC
*
* This driver provides the ability to view and manage Type C ports through the
* Chrome OS EC.
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/usb/typec.h>
#define DRV_NAME "cros-ec-typec"
/* Platform-specific data for the Chrome OS EC Type C controller. */
struct cros_typec_data {
struct device *dev;
struct cros_ec_device *ec;
int num_ports;
unsigned int cmd_ver;
/* Array of ports, indexed by port number. */
struct typec_port *ports[EC_USB_PD_MAX_PORTS];
/* Initial capabilities for each port. */
struct typec_capability *caps[EC_USB_PD_MAX_PORTS];
};
static int cros_typec_parse_port_props(struct typec_capability *cap,
struct fwnode_handle *fwnode,
struct device *dev)
{
const char *buf;
int ret;
memset(cap, 0, sizeof(*cap));
ret = fwnode_property_read_string(fwnode, "power-role", &buf);
if (ret) {
dev_err(dev, "power-role not found: %d\n", ret);
return ret;
}
ret = typec_find_port_power_role(buf);
if (ret < 0)
return ret;
cap->type = ret;
ret = fwnode_property_read_string(fwnode, "data-role", &buf);
if (ret) {
dev_err(dev, "data-role not found: %d\n", ret);
return ret;
}
ret = typec_find_port_data_role(buf);
if (ret < 0)
return ret;
cap->data = ret;
ret = fwnode_property_read_string(fwnode, "try-power-role", &buf);
if (ret) {
dev_err(dev, "try-power-role not found: %d\n", ret);
return ret;
}
ret = typec_find_power_role(buf);
if (ret < 0)
return ret;
cap->prefer_role = ret;
cap->fwnode = fwnode;
return 0;
}
static int cros_typec_init_ports(struct cros_typec_data *typec)
{
struct device *dev = typec->dev;
struct typec_capability *cap;
struct fwnode_handle *fwnode;
const char *port_prop;
int ret;
int i;
int nports;
u32 port_num = 0;
nports = device_get_child_node_count(dev);
if (nports == 0) {
dev_err(dev, "No port entries found.\n");
return -ENODEV;
}
if (nports > typec->num_ports) {
dev_err(dev, "More ports listed than can be supported.\n");
return -EINVAL;
}
/* DT uses "reg" to specify port number. */
port_prop = dev->of_node ? "reg" : "port-number";
device_for_each_child_node(dev, fwnode) {
if (fwnode_property_read_u32(fwnode, port_prop, &port_num)) {
ret = -EINVAL;
dev_err(dev, "No port-number for port, aborting.\n");
goto unregister_ports;
}
if (port_num >= typec->num_ports) {
dev_err(dev, "Invalid port number.\n");
ret = -EINVAL;
goto unregister_ports;
}
dev_dbg(dev, "Registering port %d\n", port_num);
cap = devm_kzalloc(dev, sizeof(*cap), GFP_KERNEL);
if (!cap) {
ret = -ENOMEM;
goto unregister_ports;
}
typec->caps[port_num] = cap;
ret = cros_typec_parse_port_props(cap, fwnode, dev);
if (ret < 0)
goto unregister_ports;
typec->ports[port_num] = typec_register_port(dev, cap);
if (IS_ERR(typec->ports[port_num])) {
dev_err(dev, "Failed to register port %d\n", port_num);
ret = PTR_ERR(typec->ports[port_num]);
goto unregister_ports;
}
}
return 0;
unregister_ports:
for (i = 0; i < typec->num_ports; i++)
typec_unregister_port(typec->ports[i]);
return ret;
}
static int cros_typec_ec_command(struct cros_typec_data *typec,
unsigned int version,
unsigned int command,
void *outdata,
unsigned int outsize,
void *indata,
unsigned int insize)
{
struct cros_ec_command *msg;
int ret;
msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->version = version;
msg->command = command;
msg->outsize = outsize;
msg->insize = insize;
if (outsize)
memcpy(msg->data, outdata, outsize);
ret = cros_ec_cmd_xfer_status(typec->ec, msg);
if (ret >= 0 && insize)
memcpy(indata, msg->data, insize);
kfree(msg);
return ret;
}
static void cros_typec_set_port_params_v0(struct cros_typec_data *typec,
int port_num, struct ec_response_usb_pd_control *resp)
{
struct typec_port *port = typec->ports[port_num];
enum typec_orientation polarity;
if (!resp->enabled)
polarity = TYPEC_ORIENTATION_NONE;
else if (!resp->polarity)
polarity = TYPEC_ORIENTATION_NORMAL;
else
polarity = TYPEC_ORIENTATION_REVERSE;
typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK);
typec_set_orientation(port, polarity);
}
static void cros_typec_set_port_params_v1(struct cros_typec_data *typec,
int port_num, struct ec_response_usb_pd_control_v1 *resp)
{
struct typec_port *port = typec->ports[port_num];
enum typec_orientation polarity;
if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
polarity = TYPEC_ORIENTATION_NONE;
else if (!resp->polarity)
polarity = TYPEC_ORIENTATION_NORMAL;
else
polarity = TYPEC_ORIENTATION_REVERSE;
typec_set_orientation(port, polarity);
typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ?
TYPEC_HOST : TYPEC_DEVICE);
typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ?
TYPEC_SOURCE : TYPEC_SINK);
typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ?
TYPEC_SOURCE : TYPEC_SINK);
}
static int cros_typec_port_update(struct cros_typec_data *typec, int port_num)
{
struct ec_params_usb_pd_control req;
struct ec_response_usb_pd_control_v1 resp;
int ret;
if (port_num < 0 || port_num >= typec->num_ports) {
dev_err(typec->dev, "cannot get status for invalid port %d\n",
port_num);
return -EINVAL;
}
req.port = port_num;
req.role = USB_PD_CTRL_ROLE_NO_CHANGE;
req.mux = USB_PD_CTRL_MUX_NO_CHANGE;
req.swap = USB_PD_CTRL_SWAP_NONE;
ret = cros_typec_ec_command(typec, typec->cmd_ver,
EC_CMD_USB_PD_CONTROL, &req, sizeof(req),
&resp, sizeof(resp));
if (ret < 0)
return ret;
dev_dbg(typec->dev, "Enabled %d: 0x%hhx\n", port_num, resp.enabled);
dev_dbg(typec->dev, "Role %d: 0x%hhx\n", port_num, resp.role);
dev_dbg(typec->dev, "Polarity %d: 0x%hhx\n", port_num, resp.polarity);
dev_dbg(typec->dev, "State %d: %s\n", port_num, resp.state);
if (typec->cmd_ver == 1)
cros_typec_set_port_params_v1(typec, port_num, &resp);
else
cros_typec_set_port_params_v0(typec, port_num,
(struct ec_response_usb_pd_control *) &resp);
return 0;
}
static int cros_typec_get_cmd_version(struct cros_typec_data *typec)
{
struct ec_params_get_cmd_versions_v1 req_v1;
struct ec_response_get_cmd_versions resp;
int ret;
/* We're interested in the PD control command version. */
req_v1.cmd = EC_CMD_USB_PD_CONTROL;
ret = cros_typec_ec_command(typec, 1, EC_CMD_GET_CMD_VERSIONS,
&req_v1, sizeof(req_v1), &resp,
sizeof(resp));
if (ret < 0)
return ret;
if (resp.version_mask & EC_VER_MASK(1))
typec->cmd_ver = 1;
else
typec->cmd_ver = 0;
dev_dbg(typec->dev, "PD Control has version mask 0x%hhx\n",
typec->cmd_ver);
return 0;
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id cros_typec_acpi_id[] = {
{ "GOOG0014", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, cros_typec_acpi_id);
#endif
#ifdef CONFIG_OF
static const struct of_device_id cros_typec_of_match[] = {
{ .compatible = "google,cros-ec-typec", },
{}
};
MODULE_DEVICE_TABLE(of, cros_typec_of_match);
#endif
static int cros_typec_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_typec_data *typec;
struct ec_response_usb_pd_ports resp;
int ret, i;
typec = devm_kzalloc(dev, sizeof(*typec), GFP_KERNEL);
if (!typec)
return -ENOMEM;
typec->dev = dev;
typec->ec = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, typec);
ret = cros_typec_get_cmd_version(typec);
if (ret < 0) {
dev_err(dev, "failed to get PD command version info\n");
return ret;
}
ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
&resp, sizeof(resp));
if (ret < 0)
return ret;
typec->num_ports = resp.num_ports;
if (typec->num_ports > EC_USB_PD_MAX_PORTS) {
dev_warn(typec->dev,
"Too many ports reported: %d, limiting to max: %d\n",
typec->num_ports, EC_USB_PD_MAX_PORTS);
typec->num_ports = EC_USB_PD_MAX_PORTS;
}
ret = cros_typec_init_ports(typec);
if (ret < 0)
return ret;
for (i = 0; i < typec->num_ports; i++) {
ret = cros_typec_port_update(typec, i);
if (ret < 0)
goto unregister_ports;
}
return 0;
unregister_ports:
for (i = 0; i < typec->num_ports; i++)
if (typec->ports[i])
typec_unregister_port(typec->ports[i]);
return ret;
}
static struct platform_driver cros_typec_driver = {
.driver = {
.name = DRV_NAME,
.acpi_match_table = ACPI_PTR(cros_typec_acpi_id),
.of_match_table = of_match_ptr(cros_typec_of_match),
},
.probe = cros_typec_probe,
};
module_platform_driver(cros_typec_driver);
MODULE_AUTHOR("Prashant Malani <pmalani@chromium.org>");
MODULE_DESCRIPTION("Chrome OS EC Type C control");
MODULE_LICENSE("GPL");
......@@ -40,7 +40,7 @@ static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
msg->outsize = para_sz;
msg->insize = resp_sz;
err = cros_ec_cmd_xfer(ecdev, msg);
err = cros_ec_cmd_xfer_status(ecdev, msg);
if (err < 0) {
dev_err(dev, "Error sending read request: %d\n", err);
kfree(msg);
......@@ -83,7 +83,7 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
msg->outsize = para_sz;
msg->insize = 0;
err = cros_ec_cmd_xfer(ecdev, msg);
err = cros_ec_cmd_xfer_status(ecdev, msg);
if (err < 0) {
dev_err(dev, "Error sending write request: %d\n", err);
kfree(msg);
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2020 Google LLC
*
* This driver serves as the receiver of cros_ec PD host events.
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_data/cros_usbpd_notify.h>
#include <linux/platform_device.h>
#define DRV_NAME "cros-usbpd-notify"
#define DRV_NAME_PLAT_ACPI "cros-usbpd-notify-acpi"
#define ACPI_DRV_NAME "GOOG0003"
static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list);
struct cros_usbpd_notify_data {
struct device *dev;
struct cros_ec_device *ec;
struct notifier_block nb;
};
/**
* cros_usbpd_register_notify - Register a notifier callback for PD events.
* @nb: Notifier block pointer to register
*
* On ACPI platforms this corresponds to host events on the ECPD
* "GOOG0003" ACPI device. On non-ACPI platforms this will filter mkbp events
* for USB PD events.
*
* Return: 0 on success or negative error code.
*/
int cros_usbpd_register_notify(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&cros_usbpd_notifier_list,
nb);
}
EXPORT_SYMBOL_GPL(cros_usbpd_register_notify);
/**
* cros_usbpd_unregister_notify - Unregister notifier callback for PD events.
* @nb: Notifier block pointer to unregister
*
* Unregister a notifier callback that was previously registered with
* cros_usbpd_register_notify().
*/
void cros_usbpd_unregister_notify(struct notifier_block *nb)
{
blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify);
/**
* cros_ec_pd_command - Send a command to the EC.
*
* @ec_dev: EC device
* @command: EC command
* @outdata: EC command output data
* @outsize: Size of outdata
* @indata: EC command input data
* @insize: Size of indata
*
* Return: >= 0 on success, negative error number on failure.
*/
static int cros_ec_pd_command(struct cros_ec_device *ec_dev,
int command,
uint8_t *outdata,
int outsize,
uint8_t *indata,
int insize)
{
struct cros_ec_command *msg;
int ret;
msg = kzalloc(sizeof(*msg) + max(insize, outsize), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->command = command;
msg->outsize = outsize;
msg->insize = insize;
if (outsize)
memcpy(msg->data, outdata, outsize);
ret = cros_ec_cmd_xfer_status(ec_dev, msg);
if (ret < 0)
goto error;
if (insize)
memcpy(indata, msg->data, insize);
error:
kfree(msg);
return ret;
}
static void cros_usbpd_get_event_and_notify(struct device *dev,
struct cros_ec_device *ec_dev)
{
struct ec_response_host_event_status host_event_status;
u32 event = 0;
int ret;
/*
* We still send a 0 event out to older devices which don't
* have the updated device heirarchy.
*/
if (!ec_dev) {
dev_dbg(dev,
"EC device inaccessible; sending 0 event status.\n");
goto send_notify;
}
/* Check for PD host events on EC. */
ret = cros_ec_pd_command(ec_dev, EC_CMD_PD_HOST_EVENT_STATUS,
NULL, 0,
(uint8_t *)&host_event_status,
sizeof(host_event_status));
if (ret < 0) {
dev_warn(dev, "Can't get host event status (err: %d)\n", ret);
goto send_notify;
}
event = host_event_status.status;
send_notify:
blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL);
}
#ifdef CONFIG_ACPI
static void cros_usbpd_notify_acpi(acpi_handle device, u32 event, void *data)
{
struct cros_usbpd_notify_data *pdnotify = data;
cros_usbpd_get_event_and_notify(pdnotify->dev, pdnotify->ec);
}
static int cros_usbpd_notify_probe_acpi(struct platform_device *pdev)
{
struct cros_usbpd_notify_data *pdnotify;
struct device *dev = &pdev->dev;
struct acpi_device *adev;
struct cros_ec_device *ec_dev;
acpi_status status;
adev = ACPI_COMPANION(dev);
pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
if (!pdnotify)
return -ENOMEM;
/* Get the EC device pointer needed to talk to the EC. */
ec_dev = dev_get_drvdata(dev->parent);
if (!ec_dev) {
/*
* We continue even for older devices which don't have the
* correct device heirarchy, namely, GOOG0003 is a child
* of GOOG0004.
*/
dev_warn(dev, "Couldn't get Chrome EC device pointer.\n");
}
pdnotify->dev = dev;
pdnotify->ec = ec_dev;
status = acpi_install_notify_handler(adev->handle,
ACPI_ALL_NOTIFY,
cros_usbpd_notify_acpi,
pdnotify);
if (ACPI_FAILURE(status)) {
dev_warn(dev, "Failed to register notify handler %08x\n",
status);
return -EINVAL;
}
return 0;
}
static int cros_usbpd_notify_remove_acpi(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acpi_device *adev = ACPI_COMPANION(dev);
acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
cros_usbpd_notify_acpi);
return 0;
}
static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = {
{ ACPI_DRV_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids);
static struct platform_driver cros_usbpd_notify_acpi_driver = {
.driver = {
.name = DRV_NAME_PLAT_ACPI,
.acpi_match_table = cros_usbpd_notify_acpi_device_ids,
},
.probe = cros_usbpd_notify_probe_acpi,
.remove = cros_usbpd_notify_remove_acpi,
};
#endif /* CONFIG_ACPI */
static int cros_usbpd_notify_plat(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *data)
{
struct cros_usbpd_notify_data *pdnotify = container_of(nb,
struct cros_usbpd_notify_data, nb);
struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
u32 host_event = cros_ec_get_host_event(ec_dev);
if (!host_event)
return NOTIFY_DONE;
if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU)) {
cros_usbpd_get_event_and_notify(pdnotify->dev, ec_dev);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static int cros_usbpd_notify_probe_plat(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
struct cros_usbpd_notify_data *pdnotify;
int ret;
pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
if (!pdnotify)
return -ENOMEM;
pdnotify->dev = dev;
pdnotify->ec = ecdev->ec_dev;
pdnotify->nb.notifier_call = cros_usbpd_notify_plat;
dev_set_drvdata(dev, pdnotify);
ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier,
&pdnotify->nb);
if (ret < 0) {
dev_err(dev, "Failed to register notifier\n");
return ret;
}
return 0;
}
static int cros_usbpd_notify_remove_plat(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
struct cros_usbpd_notify_data *pdnotify =
(struct cros_usbpd_notify_data *)dev_get_drvdata(dev);
blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier,
&pdnotify->nb);
return 0;
}
static struct platform_driver cros_usbpd_notify_plat_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_usbpd_notify_probe_plat,
.remove = cros_usbpd_notify_remove_plat,
};
static int __init cros_usbpd_notify_init(void)
{
int ret;
ret = platform_driver_register(&cros_usbpd_notify_plat_driver);
if (ret < 0)
return ret;
#ifdef CONFIG_ACPI
platform_driver_register(&cros_usbpd_notify_acpi_driver);
#endif
return 0;
}
static void __exit cros_usbpd_notify_exit(void)
{
#ifdef CONFIG_ACPI
platform_driver_unregister(&cros_usbpd_notify_acpi_driver);
#endif
platform_driver_unregister(&cros_usbpd_notify_plat_driver);
}
module_init(cros_usbpd_notify_init);
module_exit(cros_usbpd_notify_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
MODULE_ALIAS("platform:" DRV_NAME);
......@@ -79,7 +79,7 @@ static DEFINE_IDA(event_ida);
struct ec_event {
u16 size;
u16 type;
u16 event[0];
u16 event[];
} __packed;
#define ec_event_num_words(ev) (ev->size - 1)
......@@ -96,7 +96,7 @@ struct ec_event_queue {
int capacity;
int head;
int tail;
struct ec_event *entries[0];
struct ec_event *entries[];
};
/* Maximum number of events to store in ec_event_queue */
......
......@@ -3,8 +3,11 @@
* Copyright 2019 Google LLC
*/
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/platform_data/wilco-ec.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/unaligned.h>
/* Operation code; what the EC should do with the property */
......
......@@ -8,8 +8,12 @@
* See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/platform_data/wilco-ec.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#define CMD_KB_CMOS 0x7C
#define SUB_CMD_KB_CMOS_AUTO_ON 0x03
......
......@@ -659,7 +659,7 @@ config CHARGER_RT9455
config CHARGER_CROS_USBPD
tristate "ChromeOS EC based USBPD charger"
depends on CROS_EC
depends on CROS_USBPD_NOTIFY
default n
help
Say Y here to enable ChromeOS EC based USBPD charger
......
......@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_data/cros_usbpd_notify.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
......@@ -517,32 +518,21 @@ static int cros_usbpd_charger_property_is_writeable(struct power_supply *psy,
}
static int cros_usbpd_charger_ec_event(struct notifier_block *nb,
unsigned long queued_during_suspend,
unsigned long host_event,
void *_notify)
{
struct cros_ec_device *ec_device;
struct charger_data *charger;
u32 host_event;
struct charger_data *charger = container_of(nb, struct charger_data,
notifier);
charger = container_of(nb, struct charger_data, notifier);
ec_device = charger->ec_device;
host_event = cros_ec_get_host_event(ec_device);
if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU)) {
cros_usbpd_charger_power_changed(charger->ports[0]->psy);
return NOTIFY_OK;
} else {
return NOTIFY_DONE;
}
cros_usbpd_charger_power_changed(charger->ports[0]->psy);
return NOTIFY_OK;
}
static void cros_usbpd_charger_unregister_notifier(void *data)
{
struct charger_data *charger = data;
struct cros_ec_device *ec_device = charger->ec_device;
blocking_notifier_chain_unregister(&ec_device->event_notifier,
&charger->notifier);
cros_usbpd_unregister_notify(&charger->notifier);
}
static int cros_usbpd_charger_probe(struct platform_device *pd)
......@@ -676,21 +666,17 @@ static int cros_usbpd_charger_probe(struct platform_device *pd)
goto fail;
}
if (ec_device->mkbp_event_supported) {
/* Get PD events from the EC */
charger->notifier.notifier_call = cros_usbpd_charger_ec_event;
ret = blocking_notifier_chain_register(
&ec_device->event_notifier,
&charger->notifier);
if (ret < 0) {
dev_warn(dev, "failed to register notifier\n");
} else {
ret = devm_add_action_or_reset(dev,
cros_usbpd_charger_unregister_notifier,
charger);
if (ret < 0)
goto fail;
}
/* Get PD events from the EC */
charger->notifier.notifier_call = cros_usbpd_charger_ec_event;
ret = cros_usbpd_register_notify(&charger->notifier);
if (ret < 0) {
dev_warn(dev, "failed to register notifier\n");
} else {
ret = devm_add_action_or_reset(dev,
cros_usbpd_charger_unregister_notifier,
charger);
if (ret < 0)
goto fail;
}
return 0;
......
......@@ -12,6 +12,7 @@
#include <linux/irqreturn.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>
enum {
CROS_EC_SENSOR_X,
......@@ -29,8 +30,7 @@ enum {
*/
#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 */
typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
/**
* struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
......@@ -50,7 +50,9 @@ enum {
* 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
* @fifo_max_event_count: Size of the EC sensor FIFO
* @frequencies: Table of known available frequencies:
* 0, Min and Max in mHz
*/
struct cros_ec_sensors_core_state {
struct cros_ec_device *ec;
......@@ -73,101 +75,34 @@ struct cros_ec_sensors_core_state {
int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data);
int curr_sampl_freq;
/* Table of known available frequencies : 0, Min and Max in mHz */
int frequencies[3];
u32 fifo_max_event_count;
int frequencies[6];
};
/**
* 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);
struct platform_device;
/**
* 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);
struct iio_dev *indio_dev, bool physical_device,
cros_ec_sensors_capture_t trigger_capture,
cros_ec_sensorhub_push_data_cb_t push_data);
/**
* 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);
int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
s16 *data,
s64 timestamp);
/**
* 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_read_avail() - get available values
* @indio_dev: pointer to state information for device
* @chan: channel specification structure table
* @vals: list of available values
* @type: type of data returned
* @length: number of data returned in the array
* @mask: specifies which values to be requested
*
* Return: an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST
*/
int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals,
......@@ -175,23 +110,12 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
int *length,
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);
extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
/* List of extended channel specification for all sensors */
extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
extern const struct attribute *cros_ec_sensor_fifo_attributes[];
#endif /* __CROS_EC_SENSORS_CORE_H */
......@@ -629,6 +629,8 @@ static inline clockid_t iio_device_get_clock(const struct iio_dev *indio_dev)
return indio_dev->clock_id;
}
int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id);
/**
* dev_to_iio_dev() - Get IIO device struct from a device struct
* @dev: The device embedded in the IIO device
......
......@@ -125,6 +125,9 @@ struct cros_ec_command {
* @host_event_wake_mask: Mask of host events that cause wake from suspend.
* @last_event_time: exact time from the hard irq when we got notified of
* a new event.
* @notifier_ready: The notifier_block to let the kernel re-query EC
* communication protocol when the EC sends
* EC_HOST_EVENT_INTERFACE_READY.
* @ec: The platform_device used by the mfd driver to interface with the
* main EC.
* @pd: The platform_device used by the mfd driver to interface with the
......@@ -166,6 +169,7 @@ struct cros_ec_device {
u32 host_event_wake_mask;
u32 last_resume_result;
ktime_t last_event_time;
struct notifier_block notifier_ready;
/* The platform devices used by the mfd driver */
struct platform_device *ec;
......
......@@ -8,8 +8,13 @@
#ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
#define __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
#include <linux/ktime.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/platform_data/cros_ec_commands.h>
struct iio_dev;
/**
* struct cros_ec_sensor_platform - ChromeOS EC sensor platform information.
* @sensor_num: Id of the sensor, as reported by the EC.
......@@ -19,12 +24,170 @@ struct cros_ec_sensor_platform {
};
/**
* typedef cros_ec_sensorhub_push_data_cb_t - Callback function to send datum
* to specific sensors.
*
* @indio_dev: The IIO device that will process the sample.
* @data: Vector array of the ring sample.
* @timestamp: Timestamp in host timespace when the sample was acquired by
* the EC.
*/
typedef int (*cros_ec_sensorhub_push_data_cb_t)(struct iio_dev *indio_dev,
s16 *data,
s64 timestamp);
struct cros_ec_sensorhub_sensor_push_data {
struct iio_dev *indio_dev;
cros_ec_sensorhub_push_data_cb_t push_data_cb;
};
enum {
CROS_EC_SENSOR_LAST_TS,
CROS_EC_SENSOR_NEW_TS,
CROS_EC_SENSOR_ALL_TS
};
struct cros_ec_sensors_ring_sample {
u8 sensor_id;
u8 flag;
s16 vector[3];
s64 timestamp;
} __packed;
/* State used for cros_ec_ring_fix_overflow */
struct cros_ec_sensors_ec_overflow_state {
s64 offset;
s64 last;
};
/* Length of the filter, how long to remember entries for */
#define CROS_EC_SENSORHUB_TS_HISTORY_SIZE 64
/**
* struct cros_ec_sensors_ts_filter_state - Timestamp filetr state.
*
* @x_offset: x is EC interrupt time. x_offset its last value.
* @y_offset: y is the difference between AP and EC time, y_offset its last
* value.
* @x_history: The past history of x, relative to x_offset.
* @y_history: The past history of y, relative to y_offset.
* @m_history: rate between y and x.
* @history_len: Amount of valid historic data in the arrays.
* @temp_buf: Temporary buffer used when updating the filter.
* @median_m: median value of m_history
* @median_error: final error to apply to AP interrupt timestamp to get the
* "true timestamp" the event occurred.
*/
struct cros_ec_sensors_ts_filter_state {
s64 x_offset, y_offset;
s64 x_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
s64 y_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
s64 m_history[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
int history_len;
s64 temp_buf[CROS_EC_SENSORHUB_TS_HISTORY_SIZE];
s64 median_m;
s64 median_error;
};
/* struct cros_ec_sensors_ts_batch_state - State of batch of a single sensor.
*
* Use to store information to batch data using median fileter information.
*
* @penul_ts: last but one batch timestamp (penultimate timestamp).
* Used for timestamp spreading calculations
* when a batch shows up.
* @penul_len: last but one batch length.
* @last_ts: Last batch timestam.
* @last_len: Last batch length.
* @newest_sensor_event: Last sensor timestamp.
*/
struct cros_ec_sensors_ts_batch_state {
s64 penul_ts;
int penul_len;
s64 last_ts;
int last_len;
s64 newest_sensor_event;
};
/*
* struct cros_ec_sensorhub - Sensor Hub device data.
*
* @dev: Device object, mostly used for logging.
* @ec: Embedded Controller where the hub is located.
* @sensor_num: Number of MEMS sensors present in the EC.
* @msg: Structure to send FIFO requests.
* @params: Pointer to parameters in msg.
* @resp: Pointer to responses in msg.
* @cmd_lock : Lock for sending msg.
* @notifier: Notifier to kick the FIFO interrupt.
* @ring: Preprocessed ring to store events.
* @fifo_timestamp: Array for event timestamp and spreading.
* @fifo_info: Copy of FIFO information coming from the EC.
* @fifo_size: Size of the ring.
* @batch_state: Per sensor information of the last batches received.
* @overflow_a: For handling timestamp overflow for a time (sensor events)
* @overflow_b: For handling timestamp overflow for b time (ec interrupts)
* @filter: Medium fileter structure.
* @tight_timestamps: Set to truen when EC support tight timestamping:
* The timestamps reported from the EC have low jitter.
* Timestamps also come before every sample. Set either
* by feature bits coming from the EC or userspace.
* @future_timestamp_count: Statistics used to compute shaved time.
* This occurs when timestamp interpolation from EC
* time to AP time accidentally puts timestamps in
* the future. These timestamps are clamped to
* `now` and these count/total_ns maintain the
* statistics for how much time was removed in a
* given period.
* @future_timestamp_total_ns: Total amount of time shaved.
* @push_data: Array of callback to send datums to iio sensor object.
*/
struct cros_ec_sensorhub {
struct device *dev;
struct cros_ec_dev *ec;
int sensor_num;
struct cros_ec_command *msg;
struct ec_params_motion_sense *params;
struct ec_response_motion_sense *resp;
struct mutex cmd_lock; /* Lock for protecting msg structure. */
struct notifier_block notifier;
struct cros_ec_sensors_ring_sample *ring;
ktime_t fifo_timestamp[CROS_EC_SENSOR_ALL_TS];
struct ec_response_motion_sense_fifo_info *fifo_info;
int fifo_size;
struct cros_ec_sensors_ts_batch_state *batch_state;
struct cros_ec_sensors_ec_overflow_state overflow_a;
struct cros_ec_sensors_ec_overflow_state overflow_b;
struct cros_ec_sensors_ts_filter_state filter;
int tight_timestamps;
s32 future_timestamp_count;
s64 future_timestamp_total_ns;
struct cros_ec_sensorhub_sensor_push_data *push_data;
};
int cros_ec_sensorhub_register_push_data(struct cros_ec_sensorhub *sensorhub,
u8 sensor_num,
struct iio_dev *indio_dev,
cros_ec_sensorhub_push_data_cb_t cb);
void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
u8 sensor_num);
int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub);
void cros_ec_sensorhub_ring_remove(void *arg);
int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
bool on);
#endif /* __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* ChromeOS EC Power Delivery Notifier Driver
*
* Copyright 2020 Google LLC
*/
#ifndef __LINUX_PLATFORM_DATA_CROS_USBPD_NOTIFY_H
#define __LINUX_PLATFORM_DATA_CROS_USBPD_NOTIFY_H
#include <linux/notifier.h>
int cros_usbpd_register_notify(struct notifier_block *nb);
void cros_usbpd_unregister_notify(struct notifier_block *nb);
#endif /* __LINUX_PLATFORM_DATA_CROS_USBPD_NOTIFY_H */
......@@ -8,8 +8,8 @@
#ifndef WILCO_EC_H
#define WILCO_EC_H
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/types.h>
/* Message flags for using the mailbox() interface */
#define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */
......@@ -17,6 +17,10 @@
/* Normal commands have a maximum 32 bytes of data */
#define EC_MAILBOX_DATA_SIZE 32
struct device;
struct resource;
struct platform_device;
/**
* struct wilco_ec_device - Wilco Embedded Controller handle.
* @dev: Device handle.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment