diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 88e5718965d640540a0d4ea0ca02e7aee561127c..a95c776f17a13aa41436c0e480d3866bc05eb114 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -233,6 +233,71 @@ int gb_control_timesync_authoritative(struct gb_control *control, NULL, 0); } +static int gb_control_bundle_pm_status_map(u8 status) +{ + switch (status) { + case GB_CONTROL_BUNDLE_PM_INVAL: + return -EINVAL; + case GB_CONTROL_BUNDLE_PM_BUSY: + return -EBUSY; + case GB_CONTROL_BUNDLE_PM_NA: + return -ENOMSG; + case GB_CONTROL_BUNDLE_PM_FAIL: + default: + return -EREMOTEIO; + } +} + +int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id) +{ + struct gb_control_bundle_pm_request request; + struct gb_control_bundle_pm_response response; + int ret; + + request.bundle_id = bundle_id; + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_SUSPEND, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send bundle suspend: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_BUNDLE_PM_OK) { + dev_err(&control->dev, + "bundle error while suspending: %d\n", response.status); + return gb_control_bundle_pm_status_map(response.status); + } + + return 0; +} + +int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id) +{ + struct gb_control_bundle_pm_request request; + struct gb_control_bundle_pm_response response; + int ret; + + request.bundle_id = bundle_id; + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_RESUME, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send bundle resume: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_BUNDLE_PM_OK) { + dev_err(&control->dev, + "bundle error while resuming: %d\n", response.status); + return gb_control_bundle_pm_status_map(response.status); + } + + return 0; +} + static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index b1e5af25352a1b5ee7540ab642a838c8208507b6..c7f34635ea92b41a58dedf8b4ebe1d5fe8304e89 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -52,5 +52,6 @@ int gb_control_timesync_get_last_event(struct gb_control *control, u64 *frame_time); int gb_control_timesync_authoritative(struct gb_control *control, u64 *frame_time); - +int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id); +int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0043b912f7208f8150a7edcd9df4ba4c97652c52..3b6fd02685297314622f869eb745ce221e3f64d8 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -126,6 +126,8 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_DISCONNECTING 0x0c #define GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT 0x0d #define GB_CONTROL_TYPE_MODE_SWITCH 0x0e +#define GB_CONTROL_TYPE_BUNDLE_SUSPEND 0x0f +#define GB_CONTROL_TYPE_BUNDLE_RESUME 0x10 struct gb_control_version_request { __u8 major; @@ -191,6 +193,25 @@ struct gb_control_timesync_get_last_event_response { __le64 frame_time; } __packed; +/* + * All Bundle power management operations use the same request and response + * layout and status codes. + */ + +#define GB_CONTROL_BUNDLE_PM_OK 0x00 +#define GB_CONTROL_BUNDLE_PM_INVAL 0x01 +#define GB_CONTROL_BUNDLE_PM_BUSY 0x02 +#define GB_CONTROL_BUNDLE_PM_FAIL 0x03 +#define GB_CONTROL_BUNDLE_PM_NA 0x04 + +struct gb_control_bundle_pm_request { + __u8 bundle_id; +} __packed; + +struct gb_control_bundle_pm_response { + __u8 status; +} __packed; + /* APBridge protocol */ /* request APB1 log */