// SPDX-License-Identifier: GPL-2.0 /* * Greybus CPort control protocol. * * Copyright 2015 Google Inc. * Copyright 2015 Linaro Ltd. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/greybus.h> /* Highest control-protocol version supported */ #define GB_CONTROL_VERSION_MAJOR 0 #define GB_CONTROL_VERSION_MINOR 1 static int gb_control_get_version(struct gb_control *control) { struct gb_interface *intf = control->connection->intf; struct gb_control_version_request request; struct gb_control_version_response response; int ret; request.major = GB_CONTROL_VERSION_MAJOR; request.minor = GB_CONTROL_VERSION_MINOR; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_VERSION, &request, sizeof(request), &response, sizeof(response)); if (ret) { dev_err(&intf->dev, "failed to get control-protocol version: %d\n", ret); return ret; } if (response.major > request.major) { dev_err(&intf->dev, "unsupported major control-protocol version (%u > %u)\n", response.major, request.major); return -ENOTSUPP; } control->protocol_major = response.major; control->protocol_minor = response.minor; dev_dbg(&intf->dev, "%s - %u.%u\n", __func__, response.major, response.minor); return 0; } static int gb_control_get_bundle_version(struct gb_control *control, struct gb_bundle *bundle) { struct gb_interface *intf = control->connection->intf; struct gb_control_bundle_version_request request; struct gb_control_bundle_version_response response; int ret; request.bundle_id = bundle->id; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_BUNDLE_VERSION, &request, sizeof(request), &response, sizeof(response)); if (ret) { dev_err(&intf->dev, "failed to get bundle %u class version: %d\n", bundle->id, ret); return ret; } bundle->class_major = response.major; bundle->class_minor = response.minor; dev_dbg(&intf->dev, "%s - %u: %u.%u\n", __func__, bundle->id, response.major, response.minor); return 0; } int gb_control_get_bundle_versions(struct gb_control *control) { struct gb_interface *intf = control->connection->intf; struct gb_bundle *bundle; int ret; if (!control->has_bundle_version) return 0; list_for_each_entry(bundle, &intf->bundles, links) { ret = gb_control_get_bundle_version(control, bundle); if (ret) return ret; } return 0; } /* Get Manifest's size from the interface */ int gb_control_get_manifest_size_operation(struct gb_interface *intf) { struct gb_control_get_manifest_size_response response; struct gb_connection *connection = intf->control->connection; int ret; ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE, NULL, 0, &response, sizeof(response)); if (ret) { dev_err(&connection->intf->dev, "failed to get manifest size: %d\n", ret); return ret; } return le16_to_cpu(response.size); } /* Reads Manifest from the interface */ int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size) { struct gb_connection *connection = intf->control->connection; return gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST, NULL, 0, manifest, size); } int gb_control_connected_operation(struct gb_control *control, u16 cport_id) { struct gb_control_connected_request request; request.cport_id = cpu_to_le16(cport_id); return gb_operation_sync(control->connection, GB_CONTROL_TYPE_CONNECTED, &request, sizeof(request), NULL, 0); } int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) { struct gb_control_disconnected_request request; request.cport_id = cpu_to_le16(cport_id); return gb_operation_sync(control->connection, GB_CONTROL_TYPE_DISCONNECTED, &request, sizeof(request), NULL, 0); } int gb_control_disconnecting_operation(struct gb_control *control, u16 cport_id) { struct gb_control_disconnecting_request *request; struct gb_operation *operation; int ret; operation = gb_operation_create_core(control->connection, GB_CONTROL_TYPE_DISCONNECTING, sizeof(*request), 0, 0, GFP_KERNEL); if (!operation) return -ENOMEM; request = operation->request->payload; request->cport_id = cpu_to_le16(cport_id); ret = gb_operation_request_send_sync(operation); if (ret) { dev_err(&control->dev, "failed to send disconnecting: %d\n", ret); } gb_operation_put(operation); return ret; } int gb_control_mode_switch_operation(struct gb_control *control) { struct gb_operation *operation; int ret; operation = gb_operation_create_core(control->connection, GB_CONTROL_TYPE_MODE_SWITCH, 0, 0, GB_OPERATION_FLAG_UNIDIRECTIONAL, GFP_KERNEL); if (!operation) return -ENOMEM; ret = gb_operation_request_send_sync(operation); if (ret) dev_err(&control->dev, "failed to send mode switch: %d\n", ret); gb_operation_put(operation); return ret; } 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 %u suspend: %d\n", bundle_id, ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { dev_err(&control->dev, "failed to suspend bundle %u: %d\n", bundle_id, 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 %u resume: %d\n", bundle_id, ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { dev_err(&control->dev, "failed to resume bundle %u: %d\n", bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } return 0; } int gb_control_bundle_deactivate(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_DEACTIVATE, &request, sizeof(request), &response, sizeof(response)); if (ret) { dev_err(&control->dev, "failed to send bundle %u deactivate: %d\n", bundle_id, ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { dev_err(&control->dev, "failed to deactivate bundle %u: %d\n", bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } return 0; } int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id) { struct gb_control_bundle_pm_request request; struct gb_control_bundle_pm_response response; int ret; if (!control->has_bundle_activate) return 0; request.bundle_id = bundle_id; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_BUNDLE_ACTIVATE, &request, sizeof(request), &response, sizeof(response)); if (ret) { dev_err(&control->dev, "failed to send bundle %u activate: %d\n", bundle_id, ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { dev_err(&control->dev, "failed to activate bundle %u: %d\n", bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } return 0; } static int gb_control_interface_pm_status_map(u8 status) { switch (status) { case GB_CONTROL_INTF_PM_BUSY: return -EBUSY; case GB_CONTROL_INTF_PM_NA: return -ENOMSG; default: return -EREMOTEIO; } } int gb_control_interface_suspend_prepare(struct gb_control *control) { struct gb_control_intf_pm_response response; int ret; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE, NULL, 0, &response, sizeof(response)); if (ret) { dev_err(&control->dev, "failed to send interface suspend prepare: %d\n", ret); return ret; } if (response.status != GB_CONTROL_INTF_PM_OK) { dev_err(&control->dev, "interface error while preparing suspend: %d\n", response.status); return gb_control_interface_pm_status_map(response.status); } return 0; } int gb_control_interface_deactivate_prepare(struct gb_control *control) { struct gb_control_intf_pm_response response; int ret; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE, NULL, 0, &response, sizeof(response)); if (ret) { dev_err(&control->dev, "failed to send interface deactivate prepare: %d\n", ret); return ret; } if (response.status != GB_CONTROL_INTF_PM_OK) { dev_err(&control->dev, "interface error while preparing deactivate: %d\n", response.status); return gb_control_interface_pm_status_map(response.status); } return 0; } int gb_control_interface_hibernate_abort(struct gb_control *control) { struct gb_control_intf_pm_response response; int ret; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT, NULL, 0, &response, sizeof(response)); if (ret) { dev_err(&control->dev, "failed to send interface aborting hibernate: %d\n", ret); return ret; } if (response.status != GB_CONTROL_INTF_PM_OK) { dev_err(&control->dev, "interface error while aborting hibernate: %d\n", response.status); return gb_control_interface_pm_status_map(response.status); } return 0; } static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_control *control = to_gb_control(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", control->vendor_string); } static DEVICE_ATTR_RO(vendor_string); static ssize_t product_string_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_control *control = to_gb_control(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", control->product_string); } static DEVICE_ATTR_RO(product_string); static struct attribute *control_attrs[] = { &dev_attr_vendor_string.attr, &dev_attr_product_string.attr, NULL, }; ATTRIBUTE_GROUPS(control); static void gb_control_release(struct device *dev) { struct gb_control *control = to_gb_control(dev); gb_connection_destroy(control->connection); kfree(control->vendor_string); kfree(control->product_string); kfree(control); } struct device_type greybus_control_type = { .name = "greybus_control", .release = gb_control_release, }; struct gb_control *gb_control_create(struct gb_interface *intf) { struct gb_connection *connection; struct gb_control *control; control = kzalloc(sizeof(*control), GFP_KERNEL); if (!control) return ERR_PTR(-ENOMEM); control->intf = intf; connection = gb_connection_create_control(intf); if (IS_ERR(connection)) { dev_err(&intf->dev, "failed to create control connection: %ld\n", PTR_ERR(connection)); kfree(control); return ERR_CAST(connection); } control->connection = connection; control->dev.parent = &intf->dev; control->dev.bus = &greybus_bus_type; control->dev.type = &greybus_control_type; control->dev.groups = control_groups; control->dev.dma_mask = intf->dev.dma_mask; device_initialize(&control->dev); dev_set_name(&control->dev, "%s.ctrl", dev_name(&intf->dev)); gb_connection_set_data(control->connection, control); return control; } int gb_control_enable(struct gb_control *control) { int ret; dev_dbg(&control->connection->intf->dev, "%s\n", __func__); ret = gb_connection_enable_tx(control->connection); if (ret) { dev_err(&control->connection->intf->dev, "failed to enable control connection: %d\n", ret); return ret; } ret = gb_control_get_version(control); if (ret) goto err_disable_connection; if (control->protocol_major > 0 || control->protocol_minor > 1) control->has_bundle_version = true; /* FIXME: use protocol version instead */ if (!(control->intf->quirks & GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE)) control->has_bundle_activate = true; return 0; err_disable_connection: gb_connection_disable(control->connection); return ret; } void gb_control_disable(struct gb_control *control) { dev_dbg(&control->connection->intf->dev, "%s\n", __func__); if (control->intf->disconnected) gb_connection_disable_forced(control->connection); else gb_connection_disable(control->connection); } int gb_control_suspend(struct gb_control *control) { gb_connection_disable(control->connection); return 0; } int gb_control_resume(struct gb_control *control) { int ret; ret = gb_connection_enable_tx(control->connection); if (ret) { dev_err(&control->connection->intf->dev, "failed to enable control connection: %d\n", ret); return ret; } return 0; } int gb_control_add(struct gb_control *control) { int ret; ret = device_add(&control->dev); if (ret) { dev_err(&control->dev, "failed to register control device: %d\n", ret); return ret; } return 0; } void gb_control_del(struct gb_control *control) { if (device_is_registered(&control->dev)) device_del(&control->dev); } struct gb_control *gb_control_get(struct gb_control *control) { get_device(&control->dev); return control; } void gb_control_put(struct gb_control *control) { put_device(&control->dev); } void gb_control_mode_switch_prepare(struct gb_control *control) { gb_connection_mode_switch_prepare(control->connection); } void gb_control_mode_switch_complete(struct gb_control *control) { gb_connection_mode_switch_complete(control->connection); }