1
0
Fork 0

chrome platform changes for 5.7

* 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
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQCtZK6p/AktxXfkOlzbaomhzOwwgUCXo1lfQAKCRBzbaomhzOw
 wo6TAQCKHOcrqq5Y9HYXs1QBx8e/0vVwe5Jh76Qi6hUVqXu56QEA65lBZ2ni8Udp
 f6jQDFkaeYaF2tkghvuNoAkFRI6/rAk=
 =j4Tt
 -----END PGP SIGNATURE-----

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
  ...
alistair/sensors
Linus Torvalds 2020-04-08 21:25:49 -07:00
commit 413a103cf6
33 changed files with 2470 additions and 346 deletions

View File

@ -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);
}

View File

@ -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,

View File

@ -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,

View File

@ -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 @@ done:
}
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");

View File

@ -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

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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/

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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,
};

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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,

View File

@ -0,0 +1,357 @@
// 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");

View File

@ -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);

View File

@ -0,0 +1,306 @@
// 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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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 {
};
/**
* struct cros_ec_sensorhub - Sensor Hub device data.
* typedef cros_ec_sensorhub_push_data_cb_t - Callback function to send datum
* to specific sensors.
*
* @ec: Embedded Controller where the hub is located.
* @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.
*/
struct cros_ec_sensorhub {
struct cros_ec_dev *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 */

View File

@ -0,0 +1,17 @@
// 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 */

View File

@ -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.