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) ...@@ -170,7 +170,8 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
if (!indio_dev) if (!indio_dev)
return -ENOMEM; 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) if (ret)
return ret; return ret;
...@@ -190,11 +191,6 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev) ...@@ -190,11 +191,6 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
state->sign[CROS_EC_SENSOR_Z] = -1; 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); return devm_iio_device_register(dev, indio_dev);
} }
......
...@@ -97,7 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev) ...@@ -97,7 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev)
if (!indio_dev) if (!indio_dev)
return -ENOMEM; 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) if (ret)
return ret; return ret;
...@@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids); ...@@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids);
static struct platform_driver cros_ec_lid_angle_platform_driver = { static struct platform_driver cros_ec_lid_angle_platform_driver = {
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.pm = &cros_ec_sensors_pm_ops,
}, },
.probe = cros_ec_lid_angle_probe, .probe = cros_ec_lid_angle_probe,
.id_table = cros_ec_lid_angle_ids, .id_table = cros_ec_lid_angle_ids,
......
...@@ -230,10 +230,14 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) ...@@ -230,10 +230,14 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
if (!indio_dev) if (!indio_dev)
return -ENOMEM; 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) if (ret)
return ret; return ret;
iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
indio_dev->info = &ec_sensors_info; indio_dev->info = &ec_sensors_info;
state = iio_priv(indio_dev); state = iio_priv(indio_dev);
for (channel = state->channels, i = CROS_EC_SENSOR_X; for (channel = state->channels, i = CROS_EC_SENSOR_X;
...@@ -245,7 +249,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) ...@@ -245,7 +249,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
BIT(IIO_CHAN_INFO_CALIBSCALE); BIT(IIO_CHAN_INFO_CALIBSCALE);
channel->info_mask_shared_by_all = channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_FREQUENCY) |
BIT(IIO_CHAN_INFO_SAMP_FREQ); BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->info_mask_shared_by_all_available = channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ); BIT(IIO_CHAN_INFO_SAMP_FREQ);
...@@ -292,11 +295,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) ...@@ -292,11 +295,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
else else
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd; 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); return devm_iio_device_register(dev, indio_dev);
} }
...@@ -317,7 +315,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids); ...@@ -317,7 +315,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
static struct platform_driver cros_ec_sensors_platform_driver = { static struct platform_driver cros_ec_sensors_platform_driver = {
.driver = { .driver = {
.name = "cros-ec-sensors", .name = "cros-ec-sensors",
.pm = &cros_ec_sensors_pm_ops,
}, },
.probe = cros_ec_sensors_probe, .probe = cros_ec_sensors_probe,
.id_table = cros_ec_sensors_ids, .id_table = cros_ec_sensors_ids,
......
...@@ -189,7 +189,12 @@ ssize_t iio_read_const_attr(struct device *dev, ...@@ -189,7 +189,12 @@ ssize_t iio_read_const_attr(struct device *dev,
} }
EXPORT_SYMBOL(iio_read_const_attr); 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; int ret;
const struct iio_event_interface *ev_int = indio_dev->event_interface; 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) ...@@ -207,6 +212,7 @@ static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
return 0; return 0;
} }
EXPORT_SYMBOL(iio_device_set_clock);
/** /**
* iio_get_time_ns() - utility function to get a time stamp for events etc * 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) ...@@ -177,10 +177,14 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
if (!indio_dev) if (!indio_dev)
return -ENOMEM; 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) if (ret)
return ret; return ret;
iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
indio_dev->info = &cros_ec_light_prox_info; indio_dev->info = &cros_ec_light_prox_info;
state = iio_priv(indio_dev); state = iio_priv(indio_dev);
state->core.type = state->core.resp->info.type; state->core.type = state->core.resp->info.type;
...@@ -189,8 +193,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) ...@@ -189,8 +193,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
/* Common part */ /* Common part */
channel->info_mask_shared_by_all = channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_SAMP_FREQ);
BIT(IIO_CHAN_INFO_FREQUENCY);
channel->info_mask_shared_by_all_available = channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ); BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS; channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
...@@ -236,11 +239,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) ...@@ -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; 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); return devm_iio_device_register(dev, indio_dev);
} }
...@@ -258,7 +256,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids); ...@@ -258,7 +256,6 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
static struct platform_driver cros_ec_light_prox_platform_driver = { static struct platform_driver cros_ec_light_prox_platform_driver = {
.driver = { .driver = {
.name = "cros-ec-light-prox", .name = "cros-ec-light-prox",
.pm = &cros_ec_sensors_pm_ops,
}, },
.probe = cros_ec_light_prox_probe, .probe = cros_ec_light_prox_probe,
.id_table = cros_ec_light_prox_ids, .id_table = cros_ec_light_prox_ids,
......
...@@ -134,10 +134,14 @@ static int cros_ec_baro_probe(struct platform_device *pdev) ...@@ -134,10 +134,14 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
if (!indio_dev) if (!indio_dev)
return -ENOMEM; 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) if (ret)
return ret; return ret;
iio_buffer_set_attrs(indio_dev->buffer, cros_ec_sensor_fifo_attributes);
indio_dev->info = &cros_ec_baro_info; indio_dev->info = &cros_ec_baro_info;
state = iio_priv(indio_dev); state = iio_priv(indio_dev);
state->core.type = state->core.resp->info.type; state->core.type = state->core.resp->info.type;
...@@ -147,8 +151,7 @@ static int cros_ec_baro_probe(struct platform_device *pdev) ...@@ -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_separate = BIT(IIO_CHAN_INFO_RAW);
channel->info_mask_shared_by_all = channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_SAMP_FREQ);
BIT(IIO_CHAN_INFO_FREQUENCY);
channel->info_mask_shared_by_all_available = channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ); BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS; channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
...@@ -182,11 +185,6 @@ static int cros_ec_baro_probe(struct platform_device *pdev) ...@@ -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; 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); return devm_iio_device_register(dev, indio_dev);
} }
......
...@@ -7,7 +7,7 @@ config MFD_CROS_EC ...@@ -7,7 +7,7 @@ config MFD_CROS_EC
tristate "Platform support for Chrome hardware (transitional)" tristate "Platform support for Chrome hardware (transitional)"
select CHROME_PLATFORMS select CHROME_PLATFORMS
select CROS_EC select CROS_EC
select CONFIG_MFD_CROS_EC_DEV select MFD_CROS_EC_DEV
depends on X86 || ARM || ARM64 || COMPILE_TEST depends on X86 || ARM || ARM64 || COMPILE_TEST
help help
This is a transitional Kconfig option and will be removed after This is a transitional Kconfig option and will be removed after
...@@ -214,6 +214,17 @@ config CROS_EC_SYSFS ...@@ -214,6 +214,17 @@ config CROS_EC_SYSFS
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cros_ec_sysfs. 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 config CROS_USBPD_LOGGER
tristate "Logging driver for USB PD charger" tristate "Logging driver for USB PD charger"
depends on CHARGER_CROS_USBPD depends on CHARGER_CROS_USBPD
...@@ -226,6 +237,20 @@ config CROS_USBPD_LOGGER ...@@ -226,6 +237,20 @@ config CROS_USBPD_LOGGER
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cros_usbpd_logger. 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" source "drivers/platform/chrome/wilco_ec/Kconfig"
endif # CHROMEOS_PLATFORMS endif # CHROMEOS_PLATFORMS
...@@ -12,6 +12,7 @@ obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o ...@@ -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_RPMSG) += cros_ec_rpmsg.o
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.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_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.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 obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
...@@ -19,8 +20,10 @@ obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_chardev.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_LIGHTBAR) += cros_ec_lightbar.o
obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o
obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.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_EC_SYSFS) += cros_ec_sysfs.o
obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.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/ obj-$(CONFIG_WILCO_EC) += wilco_ec/
...@@ -103,7 +103,7 @@ chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter, ...@@ -103,7 +103,7 @@ chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
pr_debug("%d-%02x is probed at %02x\n", pr_debug("%d-%02x is probed at %02x\n",
adapter->nr, info->addr, dummy->addr); adapter->nr, info->addr, dummy->addr);
i2c_unregister_device(dummy); 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) ...@@ -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; 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. */ /* For now, report failure to transition to S0ix with a warning. */
if (ret >= 0 && ec_dev->host_sleep_v1 && 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) ...@@ -138,6 +138,24 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
return ret; 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. * cros_ec_register() - Register a new ChromeOS EC, using the provided info.
* @ec_dev: Device to register. * @ec_dev: Device to register.
...@@ -237,6 +255,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev) ...@@ -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", dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec",
err); 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"); dev_info(dev, "Chrome EC device registered\n");
return 0; return 0;
......
...@@ -48,7 +48,7 @@ struct ec_event { ...@@ -48,7 +48,7 @@ struct ec_event {
struct list_head node; struct list_head node;
size_t size; size_t size;
u8 event_type; u8 event_type;
u8 data[0]; u8 data[];
}; };
static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) 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) ...@@ -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; 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. */ /* Only copy data to userland if data was received. */
if (ret < 0) if (ret < 0)
goto exit; goto exit;
......
...@@ -116,7 +116,7 @@ static int get_lightbar_version(struct cros_ec_dev *ec, ...@@ -116,7 +116,7 @@ static int get_lightbar_version(struct cros_ec_dev *ec,
param = (struct ec_params_lightbar *)msg->data; param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_VERSION; 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) { if (ret < 0) {
ret = 0; ret = 0;
goto exit; goto exit;
...@@ -193,15 +193,10 @@ static ssize_t brightness_store(struct device *dev, ...@@ -193,15 +193,10 @@ static ssize_t brightness_store(struct device *dev,
if (ret) if (ret)
goto exit; goto exit;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret < 0)
goto exit; goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto exit;
}
ret = count; ret = count;
exit: exit:
kfree(msg); kfree(msg);
...@@ -258,13 +253,10 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, ...@@ -258,13 +253,10 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
goto exit; goto exit;
} }
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret < 0)
goto exit; goto exit;
if (msg->result != EC_RES_SUCCESS)
goto exit;
i = 0; i = 0;
ok = 1; ok = 1;
} }
...@@ -305,14 +297,13 @@ static ssize_t sequence_show(struct device *dev, ...@@ -305,14 +297,13 @@ static ssize_t sequence_show(struct device *dev,
if (ret) if (ret)
goto exit; goto exit;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret == -EPROTO) {
goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = scnprintf(buf, PAGE_SIZE, ret = scnprintf(buf, PAGE_SIZE,
"ERROR: EC returned %d\n", msg->result); "ERROR: EC returned %d\n", msg->result);
goto exit; goto exit;
} else if (ret < 0) {
goto exit;
} }
resp = (struct ec_response_lightbar *)msg->data; 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) ...@@ -344,13 +335,10 @@ static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
if (ret) if (ret)
goto error; goto error;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret < 0)
goto error; goto error;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto error;
}
ret = 0; ret = 0;
error: error:
kfree(msg); kfree(msg);
...@@ -377,13 +365,10 @@ static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) ...@@ -377,13 +365,10 @@ static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
if (ret) if (ret)
goto error; goto error;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret < 0)
goto error; goto error;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto error;
}
ret = 0; ret = 0;
error: error:
kfree(msg); kfree(msg);
...@@ -425,15 +410,10 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, ...@@ -425,15 +410,10 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
if (ret) if (ret)
goto exit; goto exit;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret < 0)
goto exit; goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto exit;
}
ret = count; ret = count;
exit: exit:
kfree(msg); kfree(msg);
...@@ -487,13 +467,9 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr, ...@@ -487,13 +467,9 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr,
*/ */
msg->outsize = count + extra_bytes; 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) if (ret < 0)
goto exit; goto exit;
if (msg->result != EC_RES_SUCCESS) {
ret = -EINVAL;
goto exit;
}
ret = count; ret = count;
exit: exit:
......
...@@ -553,7 +553,10 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer); ...@@ -553,7 +553,10 @@ EXPORT_SYMBOL(cros_ec_cmd_xfer);
* replied with success status. It's not necessary to check msg->result when * replied with success status. It's not necessary to check msg->result when
* using this function. * 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, int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg) struct cros_ec_command *msg)
...@@ -563,6 +566,10 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, ...@@ -563,6 +566,10 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
ret = cros_ec_cmd_xfer(ec_dev, msg); ret = cros_ec_cmd_xfer(ec_dev, msg);
if (ret < 0) { if (ret < 0) {
dev_err(ec_dev->dev, "Command xfer error (err:%d)\n", ret); 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) { } else if (msg->result != EC_RES_SUCCESS) {
dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result); dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result);
return -EPROTO; return -EPROTO;
......
...@@ -44,6 +44,8 @@ struct cros_ec_rpmsg { ...@@ -44,6 +44,8 @@ struct cros_ec_rpmsg {
struct completion xfer_ack; struct completion xfer_ack;
struct work_struct host_event_work; struct work_struct host_event_work;
struct rpmsg_endpoint *ept; 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, ...@@ -177,7 +179,14 @@ static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
memcpy(ec_dev->din, resp->data, len); memcpy(ec_dev->din, resp->data, len);
complete(&ec_rpmsg->xfer_ack); complete(&ec_rpmsg->xfer_ack);
} else if (resp->type == HOST_EVENT_MARK) { } 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 { } else {
dev_warn(ec_dev->dev, "rpmsg received invalid type = %d", dev_warn(ec_dev->dev, "rpmsg received invalid type = %d",
resp->type); resp->type);
...@@ -240,6 +249,11 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev) ...@@ -240,6 +249,11 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
return ret; return ret;
} }
ec_rpmsg->probe_done = true;
if (ec_rpmsg->has_pending_host_event)
schedule_work(&ec_rpmsg->host_event_work);
return 0; return 0;
} }
......
...@@ -50,10 +50,8 @@ static int cros_ec_sensorhub_register(struct device *dev, ...@@ -50,10 +50,8 @@ static int cros_ec_sensorhub_register(struct device *dev,
struct cros_ec_sensorhub *sensorhub) struct cros_ec_sensorhub *sensorhub)
{ {
int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 }; int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
struct cros_ec_command *msg = sensorhub->msg;
struct cros_ec_dev *ec = sensorhub->ec; 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; int ret, i, sensor_num;
char *name; char *name;
...@@ -65,27 +63,19 @@ static int cros_ec_sensorhub_register(struct device *dev, ...@@ -65,27 +63,19 @@ static int cros_ec_sensorhub_register(struct device *dev,
return sensor_num; return sensor_num;
} }
sensorhub->sensor_num = sensor_num;
if (sensor_num == 0) { if (sensor_num == 0) {
dev_err(dev, "Zero sensors reported.\n"); dev_err(dev, "Zero sensors reported.\n");
return -EINVAL; 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->version = 1;
msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; msg->insize = sizeof(struct ec_response_motion_sense);
msg->outsize = sizeof(*params); msg->outsize = sizeof(struct ec_params_motion_sense);
msg->insize = sizeof(*resp);
params = (struct ec_params_motion_sense *)msg->data;
resp = (struct ec_response_motion_sense *)msg->data;
for (i = 0; i < sensor_num; i++) { for (i = 0; i < sensor_num; i++) {
params->cmd = MOTIONSENSE_CMD_INFO; sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
params->info.sensor_num = i; sensorhub->params->info.sensor_num = i;
ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) { if (ret < 0) {
...@@ -94,7 +84,7 @@ static int cros_ec_sensorhub_register(struct device *dev, ...@@ -94,7 +84,7 @@ static int cros_ec_sensorhub_register(struct device *dev,
continue; continue;
} }
switch (resp->info.type) { switch (sensorhub->resp->info.type) {
case MOTIONSENSE_TYPE_ACCEL: case MOTIONSENSE_TYPE_ACCEL:
name = "cros-ec-accel"; name = "cros-ec-accel";
break; break;
...@@ -117,15 +107,16 @@ static int cros_ec_sensorhub_register(struct device *dev, ...@@ -117,15 +107,16 @@ static int cros_ec_sensorhub_register(struct device *dev,
name = "cros-ec-activity"; name = "cros-ec-activity";
break; break;
default: default:
dev_warn(dev, "unknown type %d\n", resp->info.type); dev_warn(dev, "unknown type %d\n",
sensorhub->resp->info.type);
continue; continue;
} }
ret = cros_ec_sensorhub_allocate_sensor(dev, name, i); ret = cros_ec_sensorhub_allocate_sensor(dev, name, i);
if (ret) if (ret)
goto error; return ret;
sensor_type[resp->info.type]++; sensor_type[sensorhub->resp->info.type]++;
} }
if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
...@@ -137,29 +128,41 @@ static int cros_ec_sensorhub_register(struct device *dev, ...@@ -137,29 +128,41 @@ static int cros_ec_sensorhub_register(struct device *dev,
"cros-ec-lid-angle", "cros-ec-lid-angle",
0); 0);
if (ret) if (ret)
goto error; return ret;
} }
kfree(msg);
return 0; return 0;
error:
kfree(msg);
return ret;
} }
static int cros_ec_sensorhub_probe(struct platform_device *pdev) static int cros_ec_sensorhub_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
struct cros_ec_sensorhub *data; struct cros_ec_sensorhub *data;
struct cros_ec_command *msg;
int ret; int ret;
int i; 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); data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; 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); dev_set_drvdata(dev, data);
/* Check whether this EC is a sensor hub. */ /* Check whether this EC is a sensor hub. */
...@@ -172,7 +175,8 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev) ...@@ -172,7 +175,8 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
* If the device has sensors but does not claim to * If the device has sensors but does not claim to
* be a sensor hub, we are in legacy mode. * 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, ret = cros_ec_sensorhub_allocate_sensor(dev,
"cros-ec-accel-legacy", i); "cros-ec-accel-legacy", i);
if (ret) if (ret)
...@@ -180,12 +184,63 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev) ...@@ -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; 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 = { static struct platform_driver cros_ec_sensorhub_driver = {
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.pm = &cros_ec_sensorhub_pm_ops,
}, },
.probe = cros_ec_sensorhub_probe, .probe = cros_ec_sensorhub_probe,
}; };
......
This diff is collapsed.
...@@ -127,7 +127,8 @@ static int terminate_request(struct cros_ec_device *ec_dev) ...@@ -127,7 +127,8 @@ static int terminate_request(struct cros_ec_device *ec_dev)
*/ */
spi_message_init(&msg); spi_message_init(&msg);
memset(&trans, 0, sizeof(trans)); 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); spi_message_add_tail(&trans, &msg);
ret = spi_sync_locked(ec_spi->spi, &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, ...@@ -416,7 +417,8 @@ static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
spi_message_init(&msg); spi_message_init(&msg);
if (ec_spi->start_of_msg_delay) { if (ec_spi->start_of_msg_delay) {
memset(&trans_delay, 0, sizeof(trans_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); spi_message_add_tail(&trans_delay, &msg);
} }
......
...@@ -149,14 +149,14 @@ static ssize_t version_show(struct device *dev, ...@@ -149,14 +149,14 @@ static ssize_t version_show(struct device *dev,
/* Get build info. */ /* Get build info. */
msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset; msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
msg->insize = EC_HOST_PARAM_SIZE; msg->insize = EC_HOST_PARAM_SIZE;
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret == -EPROTO) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: XFER ERROR %d\n", ret);
else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count, count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: EC error %d\n", msg->result); "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'; msg->data[EC_HOST_PARAM_SIZE - 1] = '\0';
count += scnprintf(buf + count, PAGE_SIZE - count, count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: %s\n", msg->data); "Build info: %s\n", msg->data);
...@@ -165,14 +165,14 @@ static ssize_t version_show(struct device *dev, ...@@ -165,14 +165,14 @@ static ssize_t version_show(struct device *dev,
/* Get chip info. */ /* Get chip info. */
msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset; msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
msg->insize = sizeof(*r_chip); msg->insize = sizeof(*r_chip);
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret == -EPROTO) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: XFER ERROR %d\n", ret);
else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count, count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: EC error %d\n", msg->result); "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 = (struct ec_response_get_chip_info *)msg->data;
r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
...@@ -189,14 +189,14 @@ static ssize_t version_show(struct device *dev, ...@@ -189,14 +189,14 @@ static ssize_t version_show(struct device *dev,
/* Get board version */ /* Get board version */
msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset; msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
msg->insize = sizeof(*r_board); msg->insize = sizeof(*r_board);
ret = cros_ec_cmd_xfer(ec->ec_dev, msg); ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) if (ret == -EPROTO) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: XFER ERROR %d\n", ret);
else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count, count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: EC error %d\n", msg->result); "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; r_board = (struct ec_response_board_version *)msg->data;
count += scnprintf(buf + count, PAGE_SIZE - count, 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, ...@@ -40,7 +40,7 @@ static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
msg->outsize = para_sz; msg->outsize = para_sz;
msg->insize = resp_sz; msg->insize = resp_sz;
err = cros_ec_cmd_xfer(ecdev, msg); err = cros_ec_cmd_xfer_status(ecdev, msg);
if (err < 0) { if (err < 0) {
dev_err(dev, "Error sending read request: %d\n", err); dev_err(dev, "Error sending read request: %d\n", err);
kfree(msg); kfree(msg);
...@@ -83,7 +83,7 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, ...@@ -83,7 +83,7 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
msg->outsize = para_sz; msg->outsize = para_sz;
msg->insize = 0; msg->insize = 0;
err = cros_ec_cmd_xfer(ecdev, msg); err = cros_ec_cmd_xfer_status(ecdev, msg);
if (err < 0) { if (err < 0) {
dev_err(dev, "Error sending write request: %d\n", err); dev_err(dev, "Error sending write request: %d\n", err);
kfree(msg); 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); ...@@ -79,7 +79,7 @@ static DEFINE_IDA(event_ida);
struct ec_event { struct ec_event {
u16 size; u16 size;
u16 type; u16 type;
u16 event[0]; u16 event[];
} __packed; } __packed;
#define ec_event_num_words(ev) (ev->size - 1) #define ec_event_num_words(ev) (ev->size - 1)
...@@ -96,7 +96,7 @@ struct ec_event_queue { ...@@ -96,7 +96,7 @@ struct ec_event_queue {
int capacity; int capacity;
int head; int head;
int tail; int tail;
struct ec_event *entries[0]; struct ec_event *entries[];
}; };
/* Maximum number of events to store in ec_event_queue */ /* Maximum number of events to store in ec_event_queue */
......
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
* Copyright 2019 Google LLC * Copyright 2019 Google LLC
*/ */
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/platform_data/wilco-ec.h> #include <linux/platform_data/wilco-ec.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/types.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
/* Operation code; what the EC should do with the property */ /* Operation code; what the EC should do with the property */
......
...@@ -8,8 +8,12 @@ ...@@ -8,8 +8,12 @@
* See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information. * 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/platform_data/wilco-ec.h>
#include <linux/string.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/types.h>
#define CMD_KB_CMOS 0x7C #define CMD_KB_CMOS 0x7C
#define SUB_CMD_KB_CMOS_AUTO_ON 0x03 #define SUB_CMD_KB_CMOS_AUTO_ON 0x03
......
...@@ -659,7 +659,7 @@ config CHARGER_RT9455 ...@@ -659,7 +659,7 @@ config CHARGER_RT9455
config CHARGER_CROS_USBPD config CHARGER_CROS_USBPD
tristate "ChromeOS EC based USBPD charger" tristate "ChromeOS EC based USBPD charger"
depends on CROS_EC depends on CROS_USBPD_NOTIFY
default n default n
help help
Say Y here to enable ChromeOS EC based USBPD charger Say Y here to enable ChromeOS EC based USBPD charger
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_data/cros_usbpd_notify.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -517,32 +518,21 @@ static int cros_usbpd_charger_property_is_writeable(struct power_supply *psy, ...@@ -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, static int cros_usbpd_charger_ec_event(struct notifier_block *nb,
unsigned long queued_during_suspend, unsigned long host_event,
void *_notify) void *_notify)
{ {
struct cros_ec_device *ec_device; struct charger_data *charger = container_of(nb, struct charger_data,
struct charger_data *charger; notifier);
u32 host_event;
charger = container_of(nb, struct charger_data, notifier); cros_usbpd_charger_power_changed(charger->ports[0]->psy);
ec_device = charger->ec_device; return NOTIFY_OK;
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;
}
} }
static void cros_usbpd_charger_unregister_notifier(void *data) static void cros_usbpd_charger_unregister_notifier(void *data)
{ {
struct charger_data *charger = data; struct charger_data *charger = data;
struct cros_ec_device *ec_device = charger->ec_device;
blocking_notifier_chain_unregister(&ec_device->event_notifier, cros_usbpd_unregister_notify(&charger->notifier);
&charger->notifier);
} }
static int cros_usbpd_charger_probe(struct platform_device *pd) static int cros_usbpd_charger_probe(struct platform_device *pd)
...@@ -676,21 +666,17 @@ 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; goto fail;
} }
if (ec_device->mkbp_event_supported) { /* Get PD events from the EC */
/* Get PD events from the EC */ charger->notifier.notifier_call = cros_usbpd_charger_ec_event;
charger->notifier.notifier_call = cros_usbpd_charger_ec_event; ret = cros_usbpd_register_notify(&charger->notifier);
ret = blocking_notifier_chain_register( if (ret < 0) {
&ec_device->event_notifier, dev_warn(dev, "failed to register notifier\n");
&charger->notifier); } else {
if (ret < 0) { ret = devm_add_action_or_reset(dev,
dev_warn(dev, "failed to register notifier\n"); cros_usbpd_charger_unregister_notifier,
} else { charger);
ret = devm_add_action_or_reset(dev, if (ret < 0)
cros_usbpd_charger_unregister_notifier, goto fail;
charger);
if (ret < 0)
goto fail;
}
} }
return 0; return 0;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_data/cros_ec_sensorhub.h>
enum { enum {
CROS_EC_SENSOR_X, CROS_EC_SENSOR_X,
...@@ -29,8 +30,7 @@ enum { ...@@ -29,8 +30,7 @@ enum {
*/ */
#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2) #define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2)
/* Minimum sampling period to use when device is suspending */ typedef irqreturn_t (*cros_ec_sensors_capture_t)(int irq, void *p);
#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */
/** /**
* struct cros_ec_sensors_core_state - state data for EC sensors IIO driver * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
...@@ -50,7 +50,9 @@ enum { ...@@ -50,7 +50,9 @@ enum {
* the timestamp. The timestamp is always last and * the timestamp. The timestamp is always last and
* is always 8-byte aligned. * is always 8-byte aligned.
* @read_ec_sensors_data: function used for accessing sensors values * @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_sensors_core_state {
struct cros_ec_device *ec; struct cros_ec_device *ec;
...@@ -73,101 +75,34 @@ struct cros_ec_sensors_core_state { ...@@ -73,101 +75,34 @@ struct cros_ec_sensors_core_state {
int (*read_ec_sensors_data)(struct iio_dev *indio_dev, int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data); unsigned long scan_mask, s16 *data);
int curr_sampl_freq; u32 fifo_max_event_count;
int frequencies[6];
/* Table of known available frequencies : 0, Min and Max in mHz */
int frequencies[3];
}; };
/**
* 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, int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
s16 *data); 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, int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
s16 *data); s16 *data);
struct platform_device; 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, 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); 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, int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
u16 opt_length); 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, int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
int *val, int *val2, long mask); 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, int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
const int **vals, const int **vals,
...@@ -175,23 +110,12 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev, ...@@ -175,23 +110,12 @@ int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
int *length, int *length,
long mask); 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, int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
int val, int val2, long mask); 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 */ /* List of extended channel specification for all sensors */
extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; 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 */ #endif /* __CROS_EC_SENSORS_CORE_H */
...@@ -629,6 +629,8 @@ static inline clockid_t iio_device_get_clock(const struct iio_dev *indio_dev) ...@@ -629,6 +629,8 @@ static inline clockid_t iio_device_get_clock(const struct iio_dev *indio_dev)
return indio_dev->clock_id; 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_to_iio_dev() - Get IIO device struct from a device struct
* @dev: The device embedded in the IIO device * @dev: The device embedded in the IIO device
......
...@@ -125,6 +125,9 @@ struct cros_ec_command { ...@@ -125,6 +125,9 @@ struct cros_ec_command {
* @host_event_wake_mask: Mask of host events that cause wake from suspend. * @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 * @last_event_time: exact time from the hard irq when we got notified of
* a new event. * 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 * @ec: The platform_device used by the mfd driver to interface with the
* main EC. * main EC.
* @pd: The platform_device used by the mfd driver to interface with the * @pd: The platform_device used by the mfd driver to interface with the
...@@ -166,6 +169,7 @@ struct cros_ec_device { ...@@ -166,6 +169,7 @@ struct cros_ec_device {
u32 host_event_wake_mask; u32 host_event_wake_mask;
u32 last_resume_result; u32 last_resume_result;
ktime_t last_event_time; ktime_t last_event_time;
struct notifier_block notifier_ready;
/* The platform devices used by the mfd driver */ /* The platform devices used by the mfd driver */
struct platform_device *ec; struct platform_device *ec;
......
...@@ -8,8 +8,13 @@ ...@@ -8,8 +8,13 @@
#ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H #ifndef __LINUX_PLATFORM_DATA_CROS_EC_SENSORHUB_H
#define __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> #include <linux/platform_data/cros_ec_commands.h>
struct iio_dev;
/** /**
* struct cros_ec_sensor_platform - ChromeOS EC sensor platform information. * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information.
* @sensor_num: Id of the sensor, as reported by the EC. * @sensor_num: Id of the sensor, as reported by the EC.
...@@ -19,12 +24,170 @@ struct cros_ec_sensor_platform { ...@@ -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. * struct cros_ec_sensorhub - Sensor Hub device data.
* *
* @dev: Device object, mostly used for logging.
* @ec: Embedded Controller where the hub is located. * @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 cros_ec_sensorhub {
struct device *dev;
struct cros_ec_dev *ec; 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 */ #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 @@ ...@@ -8,8 +8,8 @@
#ifndef WILCO_EC_H #ifndef WILCO_EC_H
#define WILCO_EC_H #define WILCO_EC_H
#include <linux/device.h> #include <linux/mutex.h>
#include <linux/kernel.h> #include <linux/types.h>
/* Message flags for using the mailbox() interface */ /* Message flags for using the mailbox() interface */
#define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */ #define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
/* Normal commands have a maximum 32 bytes of data */ /* Normal commands have a maximum 32 bytes of data */
#define EC_MAILBOX_DATA_SIZE 32 #define EC_MAILBOX_DATA_SIZE 32
struct device;
struct resource;
struct platform_device;
/** /**
* struct wilco_ec_device - Wilco Embedded Controller handle. * struct wilco_ec_device - Wilco Embedded Controller handle.
* @dev: Device 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