18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SVC Greybus driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Google Inc. 68c2ecf20Sopenharmony_ci * Copyright 2015 Linaro Ltd. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 108c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 118c2ecf20Sopenharmony_ci#include <linux/greybus.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define SVC_INTF_EJECT_TIMEOUT 9000 148c2ecf20Sopenharmony_ci#define SVC_INTF_ACTIVATE_TIMEOUT 6000 158c2ecf20Sopenharmony_ci#define SVC_INTF_RESUME_TIMEOUT 3000 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct gb_svc_deferred_request { 188c2ecf20Sopenharmony_ci struct work_struct work; 198c2ecf20Sopenharmony_ci struct gb_operation *operation; 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int gb_svc_queue_deferred_request(struct gb_operation *operation); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic ssize_t endo_id_show(struct device *dev, 258c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return sprintf(buf, "0x%04x\n", svc->endo_id); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(endo_id); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic ssize_t ap_intf_id_show(struct device *dev, 348c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", svc->ap_intf_id); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ap_intf_id); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci// FIXME 438c2ecf20Sopenharmony_ci// This is a hack, we need to do this "right" and clean the interface up 448c2ecf20Sopenharmony_ci// properly, not just forcibly yank the thing out of the system and hope for the 458c2ecf20Sopenharmony_ci// best. But for now, people want their modules to come out without having to 468c2ecf20Sopenharmony_ci// throw the thing to the ground or get out a screwdriver. 478c2ecf20Sopenharmony_cistatic ssize_t intf_eject_store(struct device *dev, 488c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 498c2ecf20Sopenharmony_ci size_t len) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 528c2ecf20Sopenharmony_ci unsigned short intf_id; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = kstrtou16(buf, 10, &intf_id); 568c2ecf20Sopenharmony_ci if (ret < 0) 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci dev_warn(dev, "Forcibly trying to eject interface %d\n", intf_id); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ret = gb_svc_intf_eject(svc, intf_id); 628c2ecf20Sopenharmony_ci if (ret < 0) 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return len; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(intf_eject); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic ssize_t watchdog_show(struct device *dev, struct device_attribute *attr, 708c2ecf20Sopenharmony_ci char *buf) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 758c2ecf20Sopenharmony_ci gb_svc_watchdog_enabled(svc) ? "enabled" : "disabled"); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic ssize_t watchdog_store(struct device *dev, 798c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 808c2ecf20Sopenharmony_ci size_t len) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 838c2ecf20Sopenharmony_ci int retval; 848c2ecf20Sopenharmony_ci bool user_request; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci retval = strtobool(buf, &user_request); 878c2ecf20Sopenharmony_ci if (retval) 888c2ecf20Sopenharmony_ci return retval; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (user_request) 918c2ecf20Sopenharmony_ci retval = gb_svc_watchdog_enable(svc); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci retval = gb_svc_watchdog_disable(svc); 948c2ecf20Sopenharmony_ci if (retval) 958c2ecf20Sopenharmony_ci return retval; 968c2ecf20Sopenharmony_ci return len; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(watchdog); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic ssize_t watchdog_action_show(struct device *dev, 1018c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) 1068c2ecf20Sopenharmony_ci return sprintf(buf, "panic\n"); 1078c2ecf20Sopenharmony_ci else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) 1088c2ecf20Sopenharmony_ci return sprintf(buf, "reset\n"); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic ssize_t watchdog_action_store(struct device *dev, 1148c2ecf20Sopenharmony_ci struct device_attribute *attr, 1158c2ecf20Sopenharmony_ci const char *buf, size_t len) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "panic")) 1208c2ecf20Sopenharmony_ci svc->action = GB_SVC_WATCHDOG_BITE_PANIC_KERNEL; 1218c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "reset")) 1228c2ecf20Sopenharmony_ci svc->action = GB_SVC_WATCHDOG_BITE_RESET_UNIPRO; 1238c2ecf20Sopenharmony_ci else 1248c2ecf20Sopenharmony_ci return -EINVAL; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return len; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(watchdog_action); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_rail_count_get_response response; 1338c2ecf20Sopenharmony_ci int ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, 1368c2ecf20Sopenharmony_ci GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET, NULL, 0, 1378c2ecf20Sopenharmony_ci &response, sizeof(response)); 1388c2ecf20Sopenharmony_ci if (ret) { 1398c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to get rail count: %d\n", ret); 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci *value = response.rail_count; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, 1498c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_rail_names_get_response *response, 1508c2ecf20Sopenharmony_ci size_t bufsize) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, 1558c2ecf20Sopenharmony_ci GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET, NULL, 0, 1568c2ecf20Sopenharmony_ci response, bufsize); 1578c2ecf20Sopenharmony_ci if (ret) { 1588c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to get rail names: %d\n", ret); 1598c2ecf20Sopenharmony_ci return ret; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (response->status != GB_SVC_OP_SUCCESS) { 1638c2ecf20Sopenharmony_ci dev_err(&svc->dev, 1648c2ecf20Sopenharmony_ci "SVC error while getting rail names: %u\n", 1658c2ecf20Sopenharmony_ci response->status); 1668c2ecf20Sopenharmony_ci return -EREMOTEIO; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, 1738c2ecf20Sopenharmony_ci u8 measurement_type, u32 *value) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_sample_get_request request; 1768c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_sample_get_response response; 1778c2ecf20Sopenharmony_ci int ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci request.rail_id = rail_id; 1808c2ecf20Sopenharmony_ci request.measurement_type = measurement_type; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_PWRMON_SAMPLE_GET, 1838c2ecf20Sopenharmony_ci &request, sizeof(request), 1848c2ecf20Sopenharmony_ci &response, sizeof(response)); 1858c2ecf20Sopenharmony_ci if (ret) { 1868c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to get rail sample: %d\n", ret); 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (response.result) { 1918c2ecf20Sopenharmony_ci dev_err(&svc->dev, 1928c2ecf20Sopenharmony_ci "UniPro error while getting rail power sample (%d %d): %d\n", 1938c2ecf20Sopenharmony_ci rail_id, measurement_type, response.result); 1948c2ecf20Sopenharmony_ci switch (response.result) { 1958c2ecf20Sopenharmony_ci case GB_SVC_PWRMON_GET_SAMPLE_INVAL: 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: 1988c2ecf20Sopenharmony_ci return -ENOMSG; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci return -EREMOTEIO; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci *value = le32_to_cpu(response.measurement); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciint gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, 2108c2ecf20Sopenharmony_ci u8 measurement_type, u32 *value) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_intf_sample_get_request request; 2138c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_intf_sample_get_response response; 2148c2ecf20Sopenharmony_ci int ret; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci request.intf_id = intf_id; 2178c2ecf20Sopenharmony_ci request.measurement_type = measurement_type; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, 2208c2ecf20Sopenharmony_ci GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET, 2218c2ecf20Sopenharmony_ci &request, sizeof(request), 2228c2ecf20Sopenharmony_ci &response, sizeof(response)); 2238c2ecf20Sopenharmony_ci if (ret) { 2248c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to get intf sample: %d\n", ret); 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (response.result) { 2298c2ecf20Sopenharmony_ci dev_err(&svc->dev, 2308c2ecf20Sopenharmony_ci "UniPro error while getting intf power sample (%d %d): %d\n", 2318c2ecf20Sopenharmony_ci intf_id, measurement_type, response.result); 2328c2ecf20Sopenharmony_ci switch (response.result) { 2338c2ecf20Sopenharmony_ci case GB_SVC_PWRMON_GET_SAMPLE_INVAL: 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: 2368c2ecf20Sopenharmony_ci return -ENOMSG; 2378c2ecf20Sopenharmony_ci default: 2388c2ecf20Sopenharmony_ci return -EREMOTEIO; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci *value = le32_to_cpu(response.measurement); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct attribute *svc_attrs[] = { 2488c2ecf20Sopenharmony_ci &dev_attr_endo_id.attr, 2498c2ecf20Sopenharmony_ci &dev_attr_ap_intf_id.attr, 2508c2ecf20Sopenharmony_ci &dev_attr_intf_eject.attr, 2518c2ecf20Sopenharmony_ci &dev_attr_watchdog.attr, 2528c2ecf20Sopenharmony_ci &dev_attr_watchdog_action.attr, 2538c2ecf20Sopenharmony_ci NULL, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(svc); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciint gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct gb_svc_intf_device_id_request request; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci request.intf_id = intf_id; 2628c2ecf20Sopenharmony_ci request.device_id = device_id; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, 2658c2ecf20Sopenharmony_ci &request, sizeof(request), NULL, 0); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciint gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct gb_svc_intf_eject_request request; 2718c2ecf20Sopenharmony_ci int ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci request.intf_id = intf_id; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* 2768c2ecf20Sopenharmony_ci * The pulse width for module release in svc is long so we need to 2778c2ecf20Sopenharmony_ci * increase the timeout so the operation will not return to soon. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci ret = gb_operation_sync_timeout(svc->connection, 2808c2ecf20Sopenharmony_ci GB_SVC_TYPE_INTF_EJECT, &request, 2818c2ecf20Sopenharmony_ci sizeof(request), NULL, 0, 2828c2ecf20Sopenharmony_ci SVC_INTF_EJECT_TIMEOUT); 2838c2ecf20Sopenharmony_ci if (ret) { 2848c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to eject interface %u\n", intf_id); 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ciint gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct gb_svc_intf_vsys_request request; 2948c2ecf20Sopenharmony_ci struct gb_svc_intf_vsys_response response; 2958c2ecf20Sopenharmony_ci int type, ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci request.intf_id = intf_id; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (enable) 3008c2ecf20Sopenharmony_ci type = GB_SVC_TYPE_INTF_VSYS_ENABLE; 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci type = GB_SVC_TYPE_INTF_VSYS_DISABLE; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, type, 3058c2ecf20Sopenharmony_ci &request, sizeof(request), 3068c2ecf20Sopenharmony_ci &response, sizeof(response)); 3078c2ecf20Sopenharmony_ci if (ret < 0) 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci if (response.result_code != GB_SVC_INTF_VSYS_OK) 3108c2ecf20Sopenharmony_ci return -EREMOTEIO; 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciint gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct gb_svc_intf_refclk_request request; 3178c2ecf20Sopenharmony_ci struct gb_svc_intf_refclk_response response; 3188c2ecf20Sopenharmony_ci int type, ret; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci request.intf_id = intf_id; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (enable) 3238c2ecf20Sopenharmony_ci type = GB_SVC_TYPE_INTF_REFCLK_ENABLE; 3248c2ecf20Sopenharmony_ci else 3258c2ecf20Sopenharmony_ci type = GB_SVC_TYPE_INTF_REFCLK_DISABLE; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, type, 3288c2ecf20Sopenharmony_ci &request, sizeof(request), 3298c2ecf20Sopenharmony_ci &response, sizeof(response)); 3308c2ecf20Sopenharmony_ci if (ret < 0) 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci if (response.result_code != GB_SVC_INTF_REFCLK_OK) 3338c2ecf20Sopenharmony_ci return -EREMOTEIO; 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciint gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct gb_svc_intf_unipro_request request; 3408c2ecf20Sopenharmony_ci struct gb_svc_intf_unipro_response response; 3418c2ecf20Sopenharmony_ci int type, ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci request.intf_id = intf_id; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (enable) 3468c2ecf20Sopenharmony_ci type = GB_SVC_TYPE_INTF_UNIPRO_ENABLE; 3478c2ecf20Sopenharmony_ci else 3488c2ecf20Sopenharmony_ci type = GB_SVC_TYPE_INTF_UNIPRO_DISABLE; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, type, 3518c2ecf20Sopenharmony_ci &request, sizeof(request), 3528c2ecf20Sopenharmony_ci &response, sizeof(response)); 3538c2ecf20Sopenharmony_ci if (ret < 0) 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci if (response.result_code != GB_SVC_INTF_UNIPRO_OK) 3568c2ecf20Sopenharmony_ci return -EREMOTEIO; 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciint gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct gb_svc_intf_activate_request request; 3638c2ecf20Sopenharmony_ci struct gb_svc_intf_activate_response response; 3648c2ecf20Sopenharmony_ci int ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci request.intf_id = intf_id; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = gb_operation_sync_timeout(svc->connection, 3698c2ecf20Sopenharmony_ci GB_SVC_TYPE_INTF_ACTIVATE, 3708c2ecf20Sopenharmony_ci &request, sizeof(request), 3718c2ecf20Sopenharmony_ci &response, sizeof(response), 3728c2ecf20Sopenharmony_ci SVC_INTF_ACTIVATE_TIMEOUT); 3738c2ecf20Sopenharmony_ci if (ret < 0) 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci if (response.status != GB_SVC_OP_SUCCESS) { 3768c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to activate interface %u: %u\n", 3778c2ecf20Sopenharmony_ci intf_id, response.status); 3788c2ecf20Sopenharmony_ci return -EREMOTEIO; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci *intf_type = response.intf_type; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint gb_svc_intf_resume(struct gb_svc *svc, u8 intf_id) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct gb_svc_intf_resume_request request; 3898c2ecf20Sopenharmony_ci struct gb_svc_intf_resume_response response; 3908c2ecf20Sopenharmony_ci int ret; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci request.intf_id = intf_id; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci ret = gb_operation_sync_timeout(svc->connection, 3958c2ecf20Sopenharmony_ci GB_SVC_TYPE_INTF_RESUME, 3968c2ecf20Sopenharmony_ci &request, sizeof(request), 3978c2ecf20Sopenharmony_ci &response, sizeof(response), 3988c2ecf20Sopenharmony_ci SVC_INTF_RESUME_TIMEOUT); 3998c2ecf20Sopenharmony_ci if (ret < 0) { 4008c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to send interface resume %u: %d\n", 4018c2ecf20Sopenharmony_ci intf_id, ret); 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (response.status != GB_SVC_OP_SUCCESS) { 4068c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to resume interface %u: %u\n", 4078c2ecf20Sopenharmony_ci intf_id, response.status); 4088c2ecf20Sopenharmony_ci return -EREMOTEIO; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ciint gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, 4158c2ecf20Sopenharmony_ci u32 *value) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct gb_svc_dme_peer_get_request request; 4188c2ecf20Sopenharmony_ci struct gb_svc_dme_peer_get_response response; 4198c2ecf20Sopenharmony_ci u16 result; 4208c2ecf20Sopenharmony_ci int ret; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci request.intf_id = intf_id; 4238c2ecf20Sopenharmony_ci request.attr = cpu_to_le16(attr); 4248c2ecf20Sopenharmony_ci request.selector = cpu_to_le16(selector); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_GET, 4278c2ecf20Sopenharmony_ci &request, sizeof(request), 4288c2ecf20Sopenharmony_ci &response, sizeof(response)); 4298c2ecf20Sopenharmony_ci if (ret) { 4308c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to get DME attribute (%u 0x%04x %u): %d\n", 4318c2ecf20Sopenharmony_ci intf_id, attr, selector, ret); 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci result = le16_to_cpu(response.result_code); 4368c2ecf20Sopenharmony_ci if (result) { 4378c2ecf20Sopenharmony_ci dev_err(&svc->dev, "UniPro error while getting DME attribute (%u 0x%04x %u): %u\n", 4388c2ecf20Sopenharmony_ci intf_id, attr, selector, result); 4398c2ecf20Sopenharmony_ci return -EREMOTEIO; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (value) 4438c2ecf20Sopenharmony_ci *value = le32_to_cpu(response.attr_value); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ciint gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, 4498c2ecf20Sopenharmony_ci u32 value) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct gb_svc_dme_peer_set_request request; 4528c2ecf20Sopenharmony_ci struct gb_svc_dme_peer_set_response response; 4538c2ecf20Sopenharmony_ci u16 result; 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci request.intf_id = intf_id; 4578c2ecf20Sopenharmony_ci request.attr = cpu_to_le16(attr); 4588c2ecf20Sopenharmony_ci request.selector = cpu_to_le16(selector); 4598c2ecf20Sopenharmony_ci request.value = cpu_to_le32(value); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_SET, 4628c2ecf20Sopenharmony_ci &request, sizeof(request), 4638c2ecf20Sopenharmony_ci &response, sizeof(response)); 4648c2ecf20Sopenharmony_ci if (ret) { 4658c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to set DME attribute (%u 0x%04x %u %u): %d\n", 4668c2ecf20Sopenharmony_ci intf_id, attr, selector, value, ret); 4678c2ecf20Sopenharmony_ci return ret; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci result = le16_to_cpu(response.result_code); 4718c2ecf20Sopenharmony_ci if (result) { 4728c2ecf20Sopenharmony_ci dev_err(&svc->dev, "UniPro error while setting DME attribute (%u 0x%04x %u %u): %u\n", 4738c2ecf20Sopenharmony_ci intf_id, attr, selector, value, result); 4748c2ecf20Sopenharmony_ci return -EREMOTEIO; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciint gb_svc_connection_create(struct gb_svc *svc, 4818c2ecf20Sopenharmony_ci u8 intf1_id, u16 cport1_id, 4828c2ecf20Sopenharmony_ci u8 intf2_id, u16 cport2_id, 4838c2ecf20Sopenharmony_ci u8 cport_flags) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct gb_svc_conn_create_request request; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci request.intf1_id = intf1_id; 4888c2ecf20Sopenharmony_ci request.cport1_id = cpu_to_le16(cport1_id); 4898c2ecf20Sopenharmony_ci request.intf2_id = intf2_id; 4908c2ecf20Sopenharmony_ci request.cport2_id = cpu_to_le16(cport2_id); 4918c2ecf20Sopenharmony_ci request.tc = 0; /* TC0 */ 4928c2ecf20Sopenharmony_ci request.flags = cport_flags; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, 4958c2ecf20Sopenharmony_ci &request, sizeof(request), NULL, 0); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_civoid gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, 4998c2ecf20Sopenharmony_ci u8 intf2_id, u16 cport2_id) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct gb_svc_conn_destroy_request request; 5028c2ecf20Sopenharmony_ci struct gb_connection *connection = svc->connection; 5038c2ecf20Sopenharmony_ci int ret; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci request.intf1_id = intf1_id; 5068c2ecf20Sopenharmony_ci request.cport1_id = cpu_to_le16(cport1_id); 5078c2ecf20Sopenharmony_ci request.intf2_id = intf2_id; 5088c2ecf20Sopenharmony_ci request.cport2_id = cpu_to_le16(cport2_id); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, 5118c2ecf20Sopenharmony_ci &request, sizeof(request), NULL, 0); 5128c2ecf20Sopenharmony_ci if (ret) { 5138c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to destroy connection (%u:%u %u:%u): %d\n", 5148c2ecf20Sopenharmony_ci intf1_id, cport1_id, intf2_id, cport2_id, ret); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* Creates bi-directional routes between the devices */ 5198c2ecf20Sopenharmony_ciint gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, 5208c2ecf20Sopenharmony_ci u8 intf2_id, u8 dev2_id) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct gb_svc_route_create_request request; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci request.intf1_id = intf1_id; 5258c2ecf20Sopenharmony_ci request.dev1_id = dev1_id; 5268c2ecf20Sopenharmony_ci request.intf2_id = intf2_id; 5278c2ecf20Sopenharmony_ci request.dev2_id = dev2_id; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, 5308c2ecf20Sopenharmony_ci &request, sizeof(request), NULL, 0); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* Destroys bi-directional routes between the devices */ 5348c2ecf20Sopenharmony_civoid gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct gb_svc_route_destroy_request request; 5378c2ecf20Sopenharmony_ci int ret; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci request.intf1_id = intf1_id; 5408c2ecf20Sopenharmony_ci request.intf2_id = intf2_id; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, 5438c2ecf20Sopenharmony_ci &request, sizeof(request), NULL, 0); 5448c2ecf20Sopenharmony_ci if (ret) { 5458c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to destroy route (%u %u): %d\n", 5468c2ecf20Sopenharmony_ci intf1_id, intf2_id, ret); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ciint gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, 5518c2ecf20Sopenharmony_ci u8 tx_mode, u8 tx_gear, u8 tx_nlanes, 5528c2ecf20Sopenharmony_ci u8 tx_amplitude, u8 tx_hs_equalizer, 5538c2ecf20Sopenharmony_ci u8 rx_mode, u8 rx_gear, u8 rx_nlanes, 5548c2ecf20Sopenharmony_ci u8 flags, u32 quirks, 5558c2ecf20Sopenharmony_ci struct gb_svc_l2_timer_cfg *local, 5568c2ecf20Sopenharmony_ci struct gb_svc_l2_timer_cfg *remote) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct gb_svc_intf_set_pwrm_request request; 5598c2ecf20Sopenharmony_ci struct gb_svc_intf_set_pwrm_response response; 5608c2ecf20Sopenharmony_ci int ret; 5618c2ecf20Sopenharmony_ci u16 result_code; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci request.intf_id = intf_id; 5668c2ecf20Sopenharmony_ci request.hs_series = hs_series; 5678c2ecf20Sopenharmony_ci request.tx_mode = tx_mode; 5688c2ecf20Sopenharmony_ci request.tx_gear = tx_gear; 5698c2ecf20Sopenharmony_ci request.tx_nlanes = tx_nlanes; 5708c2ecf20Sopenharmony_ci request.tx_amplitude = tx_amplitude; 5718c2ecf20Sopenharmony_ci request.tx_hs_equalizer = tx_hs_equalizer; 5728c2ecf20Sopenharmony_ci request.rx_mode = rx_mode; 5738c2ecf20Sopenharmony_ci request.rx_gear = rx_gear; 5748c2ecf20Sopenharmony_ci request.rx_nlanes = rx_nlanes; 5758c2ecf20Sopenharmony_ci request.flags = flags; 5768c2ecf20Sopenharmony_ci request.quirks = cpu_to_le32(quirks); 5778c2ecf20Sopenharmony_ci if (local) 5788c2ecf20Sopenharmony_ci request.local_l2timerdata = *local; 5798c2ecf20Sopenharmony_ci if (remote) 5808c2ecf20Sopenharmony_ci request.remote_l2timerdata = *remote; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, 5838c2ecf20Sopenharmony_ci &request, sizeof(request), 5848c2ecf20Sopenharmony_ci &response, sizeof(response)); 5858c2ecf20Sopenharmony_ci if (ret < 0) 5868c2ecf20Sopenharmony_ci return ret; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci result_code = response.result_code; 5898c2ecf20Sopenharmony_ci if (result_code != GB_SVC_SETPWRM_PWR_LOCAL) { 5908c2ecf20Sopenharmony_ci dev_err(&svc->dev, "set power mode = %d\n", result_code); 5918c2ecf20Sopenharmony_ci return -EIO; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciint gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct gb_svc_intf_set_pwrm_request request; 6018c2ecf20Sopenharmony_ci struct gb_svc_intf_set_pwrm_response response; 6028c2ecf20Sopenharmony_ci int ret; 6038c2ecf20Sopenharmony_ci u16 result_code; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci request.intf_id = intf_id; 6088c2ecf20Sopenharmony_ci request.hs_series = GB_SVC_UNIPRO_HS_SERIES_A; 6098c2ecf20Sopenharmony_ci request.tx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; 6108c2ecf20Sopenharmony_ci request.rx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, 6138c2ecf20Sopenharmony_ci &request, sizeof(request), 6148c2ecf20Sopenharmony_ci &response, sizeof(response)); 6158c2ecf20Sopenharmony_ci if (ret < 0) { 6168c2ecf20Sopenharmony_ci dev_err(&svc->dev, 6178c2ecf20Sopenharmony_ci "failed to send set power mode operation to interface %u: %d\n", 6188c2ecf20Sopenharmony_ci intf_id, ret); 6198c2ecf20Sopenharmony_ci return ret; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci result_code = response.result_code; 6238c2ecf20Sopenharmony_ci if (result_code != GB_SVC_SETPWRM_PWR_OK) { 6248c2ecf20Sopenharmony_ci dev_err(&svc->dev, 6258c2ecf20Sopenharmony_ci "failed to hibernate the link for interface %u: %u\n", 6268c2ecf20Sopenharmony_ci intf_id, result_code); 6278c2ecf20Sopenharmony_ci return -EIO; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ciint gb_svc_ping(struct gb_svc *svc) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PING, 6368c2ecf20Sopenharmony_ci NULL, 0, NULL, 0, 6378c2ecf20Sopenharmony_ci GB_OPERATION_TIMEOUT_DEFAULT * 2); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int gb_svc_version_request(struct gb_operation *op) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct gb_connection *connection = op->connection; 6438c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 6448c2ecf20Sopenharmony_ci struct gb_svc_version_request *request; 6458c2ecf20Sopenharmony_ci struct gb_svc_version_response *response; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (op->request->payload_size < sizeof(*request)) { 6488c2ecf20Sopenharmony_ci dev_err(&svc->dev, "short version request (%zu < %zu)\n", 6498c2ecf20Sopenharmony_ci op->request->payload_size, 6508c2ecf20Sopenharmony_ci sizeof(*request)); 6518c2ecf20Sopenharmony_ci return -EINVAL; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci request = op->request->payload; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (request->major > GB_SVC_VERSION_MAJOR) { 6578c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unsupported major version (%u > %u)\n", 6588c2ecf20Sopenharmony_ci request->major, GB_SVC_VERSION_MAJOR); 6598c2ecf20Sopenharmony_ci return -ENOTSUPP; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci svc->protocol_major = request->major; 6638c2ecf20Sopenharmony_ci svc->protocol_minor = request->minor; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) 6668c2ecf20Sopenharmony_ci return -ENOMEM; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci response = op->response->payload; 6698c2ecf20Sopenharmony_ci response->major = svc->protocol_major; 6708c2ecf20Sopenharmony_ci response->minor = svc->protocol_minor; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic ssize_t pwr_debugfs_voltage_read(struct file *file, char __user *buf, 6768c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct svc_debugfs_pwrmon_rail *pwrmon_rails = 6798c2ecf20Sopenharmony_ci file_inode(file)->i_private; 6808c2ecf20Sopenharmony_ci struct gb_svc *svc = pwrmon_rails->svc; 6818c2ecf20Sopenharmony_ci int ret, desc; 6828c2ecf20Sopenharmony_ci u32 value; 6838c2ecf20Sopenharmony_ci char buff[16]; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, 6868c2ecf20Sopenharmony_ci GB_SVC_PWRMON_TYPE_VOL, &value); 6878c2ecf20Sopenharmony_ci if (ret) { 6888c2ecf20Sopenharmony_ci dev_err(&svc->dev, 6898c2ecf20Sopenharmony_ci "failed to get voltage sample %u: %d\n", 6908c2ecf20Sopenharmony_ci pwrmon_rails->id, ret); 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci desc = scnprintf(buff, sizeof(buff), "%u\n", value); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, len, offset, buff, desc); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic ssize_t pwr_debugfs_current_read(struct file *file, char __user *buf, 7008c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct svc_debugfs_pwrmon_rail *pwrmon_rails = 7038c2ecf20Sopenharmony_ci file_inode(file)->i_private; 7048c2ecf20Sopenharmony_ci struct gb_svc *svc = pwrmon_rails->svc; 7058c2ecf20Sopenharmony_ci int ret, desc; 7068c2ecf20Sopenharmony_ci u32 value; 7078c2ecf20Sopenharmony_ci char buff[16]; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, 7108c2ecf20Sopenharmony_ci GB_SVC_PWRMON_TYPE_CURR, &value); 7118c2ecf20Sopenharmony_ci if (ret) { 7128c2ecf20Sopenharmony_ci dev_err(&svc->dev, 7138c2ecf20Sopenharmony_ci "failed to get current sample %u: %d\n", 7148c2ecf20Sopenharmony_ci pwrmon_rails->id, ret); 7158c2ecf20Sopenharmony_ci return ret; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci desc = scnprintf(buff, sizeof(buff), "%u\n", value); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, len, offset, buff, desc); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic ssize_t pwr_debugfs_power_read(struct file *file, char __user *buf, 7248c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct svc_debugfs_pwrmon_rail *pwrmon_rails = 7278c2ecf20Sopenharmony_ci file_inode(file)->i_private; 7288c2ecf20Sopenharmony_ci struct gb_svc *svc = pwrmon_rails->svc; 7298c2ecf20Sopenharmony_ci int ret, desc; 7308c2ecf20Sopenharmony_ci u32 value; 7318c2ecf20Sopenharmony_ci char buff[16]; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, 7348c2ecf20Sopenharmony_ci GB_SVC_PWRMON_TYPE_PWR, &value); 7358c2ecf20Sopenharmony_ci if (ret) { 7368c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to get power sample %u: %d\n", 7378c2ecf20Sopenharmony_ci pwrmon_rails->id, ret); 7388c2ecf20Sopenharmony_ci return ret; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci desc = scnprintf(buff, sizeof(buff), "%u\n", value); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, len, offset, buff, desc); 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic const struct file_operations pwrmon_debugfs_voltage_fops = { 7478c2ecf20Sopenharmony_ci .read = pwr_debugfs_voltage_read, 7488c2ecf20Sopenharmony_ci}; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic const struct file_operations pwrmon_debugfs_current_fops = { 7518c2ecf20Sopenharmony_ci .read = pwr_debugfs_current_read, 7528c2ecf20Sopenharmony_ci}; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic const struct file_operations pwrmon_debugfs_power_fops = { 7558c2ecf20Sopenharmony_ci .read = pwr_debugfs_power_read, 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int i; 7618c2ecf20Sopenharmony_ci size_t bufsize; 7628c2ecf20Sopenharmony_ci struct dentry *dent; 7638c2ecf20Sopenharmony_ci struct gb_svc_pwrmon_rail_names_get_response *rail_names; 7648c2ecf20Sopenharmony_ci u8 rail_count; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci dent = debugfs_create_dir("pwrmon", svc->debugfs_dentry); 7678c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dent)) 7688c2ecf20Sopenharmony_ci return; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (gb_svc_pwrmon_rail_count_get(svc, &rail_count)) 7718c2ecf20Sopenharmony_ci goto err_pwrmon_debugfs; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) 7748c2ecf20Sopenharmony_ci goto err_pwrmon_debugfs; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci bufsize = sizeof(*rail_names) + 7778c2ecf20Sopenharmony_ci GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci rail_names = kzalloc(bufsize, GFP_KERNEL); 7808c2ecf20Sopenharmony_ci if (!rail_names) 7818c2ecf20Sopenharmony_ci goto err_pwrmon_debugfs; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci svc->pwrmon_rails = kcalloc(rail_count, sizeof(*svc->pwrmon_rails), 7848c2ecf20Sopenharmony_ci GFP_KERNEL); 7858c2ecf20Sopenharmony_ci if (!svc->pwrmon_rails) 7868c2ecf20Sopenharmony_ci goto err_pwrmon_debugfs_free; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (gb_svc_pwrmon_rail_names_get(svc, rail_names, bufsize)) 7898c2ecf20Sopenharmony_ci goto err_pwrmon_debugfs_free; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci for (i = 0; i < rail_count; i++) { 7928c2ecf20Sopenharmony_ci struct dentry *dir; 7938c2ecf20Sopenharmony_ci struct svc_debugfs_pwrmon_rail *rail = &svc->pwrmon_rails[i]; 7948c2ecf20Sopenharmony_ci char fname[GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci snprintf(fname, sizeof(fname), "%s", 7978c2ecf20Sopenharmony_ci (char *)&rail_names->name[i]); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci rail->id = i; 8008c2ecf20Sopenharmony_ci rail->svc = svc; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci dir = debugfs_create_dir(fname, dent); 8038c2ecf20Sopenharmony_ci debugfs_create_file("voltage_now", 0444, dir, rail, 8048c2ecf20Sopenharmony_ci &pwrmon_debugfs_voltage_fops); 8058c2ecf20Sopenharmony_ci debugfs_create_file("current_now", 0444, dir, rail, 8068c2ecf20Sopenharmony_ci &pwrmon_debugfs_current_fops); 8078c2ecf20Sopenharmony_ci debugfs_create_file("power_now", 0444, dir, rail, 8088c2ecf20Sopenharmony_ci &pwrmon_debugfs_power_fops); 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci kfree(rail_names); 8128c2ecf20Sopenharmony_ci return; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cierr_pwrmon_debugfs_free: 8158c2ecf20Sopenharmony_ci kfree(rail_names); 8168c2ecf20Sopenharmony_ci kfree(svc->pwrmon_rails); 8178c2ecf20Sopenharmony_ci svc->pwrmon_rails = NULL; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cierr_pwrmon_debugfs: 8208c2ecf20Sopenharmony_ci debugfs_remove(dent); 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic void gb_svc_debugfs_init(struct gb_svc *svc) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci svc->debugfs_dentry = debugfs_create_dir(dev_name(&svc->dev), 8268c2ecf20Sopenharmony_ci gb_debugfs_get()); 8278c2ecf20Sopenharmony_ci gb_svc_pwrmon_debugfs_init(svc); 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic void gb_svc_debugfs_exit(struct gb_svc *svc) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci debugfs_remove_recursive(svc->debugfs_dentry); 8338c2ecf20Sopenharmony_ci kfree(svc->pwrmon_rails); 8348c2ecf20Sopenharmony_ci svc->pwrmon_rails = NULL; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic int gb_svc_hello(struct gb_operation *op) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct gb_connection *connection = op->connection; 8408c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 8418c2ecf20Sopenharmony_ci struct gb_svc_hello_request *hello_request; 8428c2ecf20Sopenharmony_ci int ret; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (op->request->payload_size < sizeof(*hello_request)) { 8458c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "short hello request (%zu < %zu)\n", 8468c2ecf20Sopenharmony_ci op->request->payload_size, 8478c2ecf20Sopenharmony_ci sizeof(*hello_request)); 8488c2ecf20Sopenharmony_ci return -EINVAL; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci hello_request = op->request->payload; 8528c2ecf20Sopenharmony_ci svc->endo_id = le16_to_cpu(hello_request->endo_id); 8538c2ecf20Sopenharmony_ci svc->ap_intf_id = hello_request->interface_id; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci ret = device_add(&svc->dev); 8568c2ecf20Sopenharmony_ci if (ret) { 8578c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to register svc device: %d\n", ret); 8588c2ecf20Sopenharmony_ci return ret; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci ret = gb_svc_watchdog_create(svc); 8628c2ecf20Sopenharmony_ci if (ret) { 8638c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); 8648c2ecf20Sopenharmony_ci goto err_unregister_device; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci gb_svc_debugfs_init(svc); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci ret = gb_svc_queue_deferred_request(op); 8708c2ecf20Sopenharmony_ci if (ret) 8718c2ecf20Sopenharmony_ci goto err_remove_debugfs; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return 0; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cierr_remove_debugfs: 8768c2ecf20Sopenharmony_ci gb_svc_debugfs_exit(svc); 8778c2ecf20Sopenharmony_cierr_unregister_device: 8788c2ecf20Sopenharmony_ci gb_svc_watchdog_destroy(svc); 8798c2ecf20Sopenharmony_ci device_del(&svc->dev); 8808c2ecf20Sopenharmony_ci return ret; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic struct gb_interface *gb_svc_interface_lookup(struct gb_svc *svc, 8848c2ecf20Sopenharmony_ci u8 intf_id) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct gb_host_device *hd = svc->hd; 8878c2ecf20Sopenharmony_ci struct gb_module *module; 8888c2ecf20Sopenharmony_ci size_t num_interfaces; 8898c2ecf20Sopenharmony_ci u8 module_id; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci list_for_each_entry(module, &hd->modules, hd_node) { 8928c2ecf20Sopenharmony_ci module_id = module->module_id; 8938c2ecf20Sopenharmony_ci num_interfaces = module->num_interfaces; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (intf_id >= module_id && 8968c2ecf20Sopenharmony_ci intf_id < module_id + num_interfaces) { 8978c2ecf20Sopenharmony_ci return module->interfaces[intf_id - module_id]; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return NULL; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci struct gb_host_device *hd = svc->hd; 9078c2ecf20Sopenharmony_ci struct gb_module *module; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci list_for_each_entry(module, &hd->modules, hd_node) { 9108c2ecf20Sopenharmony_ci if (module->module_id == module_id) 9118c2ecf20Sopenharmony_ci return module; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci return NULL; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic void gb_svc_process_hello_deferred(struct gb_operation *operation) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct gb_connection *connection = operation->connection; 9208c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 9218c2ecf20Sopenharmony_ci int ret; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * XXX This is a hack/work-around to reconfigure the APBridgeA-Switch 9258c2ecf20Sopenharmony_ci * link to PWM G2, 1 Lane, Slow Auto, so that it has sufficient 9268c2ecf20Sopenharmony_ci * bandwidth for 3 audio streams plus boot-over-UniPro of a hot-plugged 9278c2ecf20Sopenharmony_ci * module. 9288c2ecf20Sopenharmony_ci * 9298c2ecf20Sopenharmony_ci * The code should be removed once SW-2217, Heuristic for UniPro 9308c2ecf20Sopenharmony_ci * Power Mode Changes is resolved. 9318c2ecf20Sopenharmony_ci */ 9328c2ecf20Sopenharmony_ci ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, 9338c2ecf20Sopenharmony_ci GB_SVC_UNIPRO_HS_SERIES_A, 9348c2ecf20Sopenharmony_ci GB_SVC_UNIPRO_SLOW_AUTO_MODE, 9358c2ecf20Sopenharmony_ci 2, 1, 9368c2ecf20Sopenharmony_ci GB_SVC_SMALL_AMPLITUDE, 9378c2ecf20Sopenharmony_ci GB_SVC_NO_DE_EMPHASIS, 9388c2ecf20Sopenharmony_ci GB_SVC_UNIPRO_SLOW_AUTO_MODE, 9398c2ecf20Sopenharmony_ci 2, 1, 9408c2ecf20Sopenharmony_ci 0, 0, 9418c2ecf20Sopenharmony_ci NULL, NULL); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (ret) 9448c2ecf20Sopenharmony_ci dev_warn(&svc->dev, 9458c2ecf20Sopenharmony_ci "power mode change failed on AP to switch link: %d\n", 9468c2ecf20Sopenharmony_ci ret); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic void gb_svc_process_module_inserted(struct gb_operation *operation) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct gb_svc_module_inserted_request *request; 9528c2ecf20Sopenharmony_ci struct gb_connection *connection = operation->connection; 9538c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 9548c2ecf20Sopenharmony_ci struct gb_host_device *hd = svc->hd; 9558c2ecf20Sopenharmony_ci struct gb_module *module; 9568c2ecf20Sopenharmony_ci size_t num_interfaces; 9578c2ecf20Sopenharmony_ci u8 module_id; 9588c2ecf20Sopenharmony_ci u16 flags; 9598c2ecf20Sopenharmony_ci int ret; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* The request message size has already been verified. */ 9628c2ecf20Sopenharmony_ci request = operation->request->payload; 9638c2ecf20Sopenharmony_ci module_id = request->primary_intf_id; 9648c2ecf20Sopenharmony_ci num_interfaces = request->intf_count; 9658c2ecf20Sopenharmony_ci flags = le16_to_cpu(request->flags); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci dev_dbg(&svc->dev, "%s - id = %u, num_interfaces = %zu, flags = 0x%04x\n", 9688c2ecf20Sopenharmony_ci __func__, module_id, num_interfaces, flags); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (flags & GB_SVC_MODULE_INSERTED_FLAG_NO_PRIMARY) { 9718c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "no primary interface detected on module %u\n", 9728c2ecf20Sopenharmony_ci module_id); 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci module = gb_svc_module_lookup(svc, module_id); 9768c2ecf20Sopenharmony_ci if (module) { 9778c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unexpected module-inserted event %u\n", 9788c2ecf20Sopenharmony_ci module_id); 9798c2ecf20Sopenharmony_ci return; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci module = gb_module_create(hd, module_id, num_interfaces); 9838c2ecf20Sopenharmony_ci if (!module) { 9848c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to create module\n"); 9858c2ecf20Sopenharmony_ci return; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci ret = gb_module_add(module); 9898c2ecf20Sopenharmony_ci if (ret) { 9908c2ecf20Sopenharmony_ci gb_module_put(module); 9918c2ecf20Sopenharmony_ci return; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci list_add(&module->hd_node, &hd->modules); 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic void gb_svc_process_module_removed(struct gb_operation *operation) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct gb_svc_module_removed_request *request; 10008c2ecf20Sopenharmony_ci struct gb_connection *connection = operation->connection; 10018c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 10028c2ecf20Sopenharmony_ci struct gb_module *module; 10038c2ecf20Sopenharmony_ci u8 module_id; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* The request message size has already been verified. */ 10068c2ecf20Sopenharmony_ci request = operation->request->payload; 10078c2ecf20Sopenharmony_ci module_id = request->primary_intf_id; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci dev_dbg(&svc->dev, "%s - id = %u\n", __func__, module_id); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci module = gb_svc_module_lookup(svc, module_id); 10128c2ecf20Sopenharmony_ci if (!module) { 10138c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unexpected module-removed event %u\n", 10148c2ecf20Sopenharmony_ci module_id); 10158c2ecf20Sopenharmony_ci return; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci module->disconnected = true; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci gb_module_del(module); 10218c2ecf20Sopenharmony_ci list_del(&module->hd_node); 10228c2ecf20Sopenharmony_ci gb_module_put(module); 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic void gb_svc_process_intf_oops(struct gb_operation *operation) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct gb_svc_intf_oops_request *request; 10288c2ecf20Sopenharmony_ci struct gb_connection *connection = operation->connection; 10298c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 10308c2ecf20Sopenharmony_ci struct gb_interface *intf; 10318c2ecf20Sopenharmony_ci u8 intf_id; 10328c2ecf20Sopenharmony_ci u8 reason; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* The request message size has already been verified. */ 10358c2ecf20Sopenharmony_ci request = operation->request->payload; 10368c2ecf20Sopenharmony_ci intf_id = request->intf_id; 10378c2ecf20Sopenharmony_ci reason = request->reason; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci intf = gb_svc_interface_lookup(svc, intf_id); 10408c2ecf20Sopenharmony_ci if (!intf) { 10418c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unexpected interface-oops event %u\n", 10428c2ecf20Sopenharmony_ci intf_id); 10438c2ecf20Sopenharmony_ci return; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci dev_info(&svc->dev, "Deactivating interface %u, interface oops reason = %u\n", 10478c2ecf20Sopenharmony_ci intf_id, reason); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci mutex_lock(&intf->mutex); 10508c2ecf20Sopenharmony_ci intf->disconnected = true; 10518c2ecf20Sopenharmony_ci gb_interface_disable(intf); 10528c2ecf20Sopenharmony_ci gb_interface_deactivate(intf); 10538c2ecf20Sopenharmony_ci mutex_unlock(&intf->mutex); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic void gb_svc_process_intf_mailbox_event(struct gb_operation *operation) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct gb_svc_intf_mailbox_event_request *request; 10598c2ecf20Sopenharmony_ci struct gb_connection *connection = operation->connection; 10608c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 10618c2ecf20Sopenharmony_ci struct gb_interface *intf; 10628c2ecf20Sopenharmony_ci u8 intf_id; 10638c2ecf20Sopenharmony_ci u16 result_code; 10648c2ecf20Sopenharmony_ci u32 mailbox; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* The request message size has already been verified. */ 10678c2ecf20Sopenharmony_ci request = operation->request->payload; 10688c2ecf20Sopenharmony_ci intf_id = request->intf_id; 10698c2ecf20Sopenharmony_ci result_code = le16_to_cpu(request->result_code); 10708c2ecf20Sopenharmony_ci mailbox = le32_to_cpu(request->mailbox); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci dev_dbg(&svc->dev, "%s - id = %u, result = 0x%04x, mailbox = 0x%08x\n", 10738c2ecf20Sopenharmony_ci __func__, intf_id, result_code, mailbox); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci intf = gb_svc_interface_lookup(svc, intf_id); 10768c2ecf20Sopenharmony_ci if (!intf) { 10778c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unexpected mailbox event %u\n", intf_id); 10788c2ecf20Sopenharmony_ci return; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci gb_interface_mailbox_event(intf, result_code, mailbox); 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic void gb_svc_process_deferred_request(struct work_struct *work) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct gb_svc_deferred_request *dr; 10878c2ecf20Sopenharmony_ci struct gb_operation *operation; 10888c2ecf20Sopenharmony_ci struct gb_svc *svc; 10898c2ecf20Sopenharmony_ci u8 type; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci dr = container_of(work, struct gb_svc_deferred_request, work); 10928c2ecf20Sopenharmony_ci operation = dr->operation; 10938c2ecf20Sopenharmony_ci svc = gb_connection_get_data(operation->connection); 10948c2ecf20Sopenharmony_ci type = operation->request->header->type; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci switch (type) { 10978c2ecf20Sopenharmony_ci case GB_SVC_TYPE_SVC_HELLO: 10988c2ecf20Sopenharmony_ci gb_svc_process_hello_deferred(operation); 10998c2ecf20Sopenharmony_ci break; 11008c2ecf20Sopenharmony_ci case GB_SVC_TYPE_MODULE_INSERTED: 11018c2ecf20Sopenharmony_ci gb_svc_process_module_inserted(operation); 11028c2ecf20Sopenharmony_ci break; 11038c2ecf20Sopenharmony_ci case GB_SVC_TYPE_MODULE_REMOVED: 11048c2ecf20Sopenharmony_ci gb_svc_process_module_removed(operation); 11058c2ecf20Sopenharmony_ci break; 11068c2ecf20Sopenharmony_ci case GB_SVC_TYPE_INTF_MAILBOX_EVENT: 11078c2ecf20Sopenharmony_ci gb_svc_process_intf_mailbox_event(operation); 11088c2ecf20Sopenharmony_ci break; 11098c2ecf20Sopenharmony_ci case GB_SVC_TYPE_INTF_OOPS: 11108c2ecf20Sopenharmony_ci gb_svc_process_intf_oops(operation); 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci default: 11138c2ecf20Sopenharmony_ci dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci gb_operation_put(operation); 11178c2ecf20Sopenharmony_ci kfree(dr); 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cistatic int gb_svc_queue_deferred_request(struct gb_operation *operation) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(operation->connection); 11238c2ecf20Sopenharmony_ci struct gb_svc_deferred_request *dr; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci dr = kmalloc(sizeof(*dr), GFP_KERNEL); 11268c2ecf20Sopenharmony_ci if (!dr) 11278c2ecf20Sopenharmony_ci return -ENOMEM; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci gb_operation_get(operation); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci dr->operation = operation; 11328c2ecf20Sopenharmony_ci INIT_WORK(&dr->work, gb_svc_process_deferred_request); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci queue_work(svc->wq, &dr->work); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic int gb_svc_intf_reset_recv(struct gb_operation *op) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(op->connection); 11428c2ecf20Sopenharmony_ci struct gb_message *request = op->request; 11438c2ecf20Sopenharmony_ci struct gb_svc_intf_reset_request *reset; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (request->payload_size < sizeof(*reset)) { 11468c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "short reset request received (%zu < %zu)\n", 11478c2ecf20Sopenharmony_ci request->payload_size, sizeof(*reset)); 11488c2ecf20Sopenharmony_ci return -EINVAL; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci reset = request->payload; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* FIXME Reset the interface here */ 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return 0; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int gb_svc_module_inserted_recv(struct gb_operation *op) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(op->connection); 11608c2ecf20Sopenharmony_ci struct gb_svc_module_inserted_request *request; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (op->request->payload_size < sizeof(*request)) { 11638c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "short module-inserted request received (%zu < %zu)\n", 11648c2ecf20Sopenharmony_ci op->request->payload_size, sizeof(*request)); 11658c2ecf20Sopenharmony_ci return -EINVAL; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci request = op->request->payload; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci dev_dbg(&svc->dev, "%s - id = %u\n", __func__, 11718c2ecf20Sopenharmony_ci request->primary_intf_id); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci return gb_svc_queue_deferred_request(op); 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic int gb_svc_module_removed_recv(struct gb_operation *op) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(op->connection); 11798c2ecf20Sopenharmony_ci struct gb_svc_module_removed_request *request; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (op->request->payload_size < sizeof(*request)) { 11828c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "short module-removed request received (%zu < %zu)\n", 11838c2ecf20Sopenharmony_ci op->request->payload_size, sizeof(*request)); 11848c2ecf20Sopenharmony_ci return -EINVAL; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci request = op->request->payload; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci dev_dbg(&svc->dev, "%s - id = %u\n", __func__, 11908c2ecf20Sopenharmony_ci request->primary_intf_id); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return gb_svc_queue_deferred_request(op); 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int gb_svc_intf_oops_recv(struct gb_operation *op) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(op->connection); 11988c2ecf20Sopenharmony_ci struct gb_svc_intf_oops_request *request; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (op->request->payload_size < sizeof(*request)) { 12018c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "short intf-oops request received (%zu < %zu)\n", 12028c2ecf20Sopenharmony_ci op->request->payload_size, sizeof(*request)); 12038c2ecf20Sopenharmony_ci return -EINVAL; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci return gb_svc_queue_deferred_request(op); 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic int gb_svc_intf_mailbox_event_recv(struct gb_operation *op) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(op->connection); 12128c2ecf20Sopenharmony_ci struct gb_svc_intf_mailbox_event_request *request; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (op->request->payload_size < sizeof(*request)) { 12158c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "short mailbox request received (%zu < %zu)\n", 12168c2ecf20Sopenharmony_ci op->request->payload_size, sizeof(*request)); 12178c2ecf20Sopenharmony_ci return -EINVAL; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci request = op->request->payload; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return gb_svc_queue_deferred_request(op); 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic int gb_svc_request_handler(struct gb_operation *op) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct gb_connection *connection = op->connection; 12308c2ecf20Sopenharmony_ci struct gb_svc *svc = gb_connection_get_data(connection); 12318c2ecf20Sopenharmony_ci u8 type = op->type; 12328c2ecf20Sopenharmony_ci int ret = 0; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* 12358c2ecf20Sopenharmony_ci * SVC requests need to follow a specific order (at least initially) and 12368c2ecf20Sopenharmony_ci * below code takes care of enforcing that. The expected order is: 12378c2ecf20Sopenharmony_ci * - PROTOCOL_VERSION 12388c2ecf20Sopenharmony_ci * - SVC_HELLO 12398c2ecf20Sopenharmony_ci * - Any other request, but the earlier two. 12408c2ecf20Sopenharmony_ci * 12418c2ecf20Sopenharmony_ci * Incoming requests are guaranteed to be serialized and so we don't 12428c2ecf20Sopenharmony_ci * need to protect 'state' for any races. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_ci switch (type) { 12458c2ecf20Sopenharmony_ci case GB_SVC_TYPE_PROTOCOL_VERSION: 12468c2ecf20Sopenharmony_ci if (svc->state != GB_SVC_STATE_RESET) 12478c2ecf20Sopenharmony_ci ret = -EINVAL; 12488c2ecf20Sopenharmony_ci break; 12498c2ecf20Sopenharmony_ci case GB_SVC_TYPE_SVC_HELLO: 12508c2ecf20Sopenharmony_ci if (svc->state != GB_SVC_STATE_PROTOCOL_VERSION) 12518c2ecf20Sopenharmony_ci ret = -EINVAL; 12528c2ecf20Sopenharmony_ci break; 12538c2ecf20Sopenharmony_ci default: 12548c2ecf20Sopenharmony_ci if (svc->state != GB_SVC_STATE_SVC_HELLO) 12558c2ecf20Sopenharmony_ci ret = -EINVAL; 12568c2ecf20Sopenharmony_ci break; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (ret) { 12608c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unexpected request 0x%02x received (state %u)\n", 12618c2ecf20Sopenharmony_ci type, svc->state); 12628c2ecf20Sopenharmony_ci return ret; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci switch (type) { 12668c2ecf20Sopenharmony_ci case GB_SVC_TYPE_PROTOCOL_VERSION: 12678c2ecf20Sopenharmony_ci ret = gb_svc_version_request(op); 12688c2ecf20Sopenharmony_ci if (!ret) 12698c2ecf20Sopenharmony_ci svc->state = GB_SVC_STATE_PROTOCOL_VERSION; 12708c2ecf20Sopenharmony_ci return ret; 12718c2ecf20Sopenharmony_ci case GB_SVC_TYPE_SVC_HELLO: 12728c2ecf20Sopenharmony_ci ret = gb_svc_hello(op); 12738c2ecf20Sopenharmony_ci if (!ret) 12748c2ecf20Sopenharmony_ci svc->state = GB_SVC_STATE_SVC_HELLO; 12758c2ecf20Sopenharmony_ci return ret; 12768c2ecf20Sopenharmony_ci case GB_SVC_TYPE_INTF_RESET: 12778c2ecf20Sopenharmony_ci return gb_svc_intf_reset_recv(op); 12788c2ecf20Sopenharmony_ci case GB_SVC_TYPE_MODULE_INSERTED: 12798c2ecf20Sopenharmony_ci return gb_svc_module_inserted_recv(op); 12808c2ecf20Sopenharmony_ci case GB_SVC_TYPE_MODULE_REMOVED: 12818c2ecf20Sopenharmony_ci return gb_svc_module_removed_recv(op); 12828c2ecf20Sopenharmony_ci case GB_SVC_TYPE_INTF_MAILBOX_EVENT: 12838c2ecf20Sopenharmony_ci return gb_svc_intf_mailbox_event_recv(op); 12848c2ecf20Sopenharmony_ci case GB_SVC_TYPE_INTF_OOPS: 12858c2ecf20Sopenharmony_ci return gb_svc_intf_oops_recv(op); 12868c2ecf20Sopenharmony_ci default: 12878c2ecf20Sopenharmony_ci dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); 12888c2ecf20Sopenharmony_ci return -EINVAL; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic void gb_svc_release(struct device *dev) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci struct gb_svc *svc = to_gb_svc(dev); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci if (svc->connection) 12978c2ecf20Sopenharmony_ci gb_connection_destroy(svc->connection); 12988c2ecf20Sopenharmony_ci ida_destroy(&svc->device_id_map); 12998c2ecf20Sopenharmony_ci destroy_workqueue(svc->wq); 13008c2ecf20Sopenharmony_ci kfree(svc); 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistruct device_type greybus_svc_type = { 13048c2ecf20Sopenharmony_ci .name = "greybus_svc", 13058c2ecf20Sopenharmony_ci .release = gb_svc_release, 13068c2ecf20Sopenharmony_ci}; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistruct gb_svc *gb_svc_create(struct gb_host_device *hd) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci struct gb_svc *svc; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci svc = kzalloc(sizeof(*svc), GFP_KERNEL); 13138c2ecf20Sopenharmony_ci if (!svc) 13148c2ecf20Sopenharmony_ci return NULL; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci svc->wq = alloc_workqueue("%s:svc", WQ_UNBOUND, 1, dev_name(&hd->dev)); 13178c2ecf20Sopenharmony_ci if (!svc->wq) { 13188c2ecf20Sopenharmony_ci kfree(svc); 13198c2ecf20Sopenharmony_ci return NULL; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci svc->dev.parent = &hd->dev; 13238c2ecf20Sopenharmony_ci svc->dev.bus = &greybus_bus_type; 13248c2ecf20Sopenharmony_ci svc->dev.type = &greybus_svc_type; 13258c2ecf20Sopenharmony_ci svc->dev.groups = svc_groups; 13268c2ecf20Sopenharmony_ci svc->dev.dma_mask = svc->dev.parent->dma_mask; 13278c2ecf20Sopenharmony_ci device_initialize(&svc->dev); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci dev_set_name(&svc->dev, "%d-svc", hd->bus_id); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci ida_init(&svc->device_id_map); 13328c2ecf20Sopenharmony_ci svc->state = GB_SVC_STATE_RESET; 13338c2ecf20Sopenharmony_ci svc->hd = hd; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, 13368c2ecf20Sopenharmony_ci gb_svc_request_handler); 13378c2ecf20Sopenharmony_ci if (IS_ERR(svc->connection)) { 13388c2ecf20Sopenharmony_ci dev_err(&svc->dev, "failed to create connection: %ld\n", 13398c2ecf20Sopenharmony_ci PTR_ERR(svc->connection)); 13408c2ecf20Sopenharmony_ci goto err_put_device; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci gb_connection_set_data(svc->connection, svc); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci return svc; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cierr_put_device: 13488c2ecf20Sopenharmony_ci put_device(&svc->dev); 13498c2ecf20Sopenharmony_ci return NULL; 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ciint gb_svc_add(struct gb_svc *svc) 13538c2ecf20Sopenharmony_ci{ 13548c2ecf20Sopenharmony_ci int ret; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* 13578c2ecf20Sopenharmony_ci * The SVC protocol is currently driven by the SVC, so the SVC device 13588c2ecf20Sopenharmony_ci * is added from the connection request handler when enough 13598c2ecf20Sopenharmony_ci * information has been received. 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_ci ret = gb_connection_enable(svc->connection); 13628c2ecf20Sopenharmony_ci if (ret) 13638c2ecf20Sopenharmony_ci return ret; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci return 0; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic void gb_svc_remove_modules(struct gb_svc *svc) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct gb_host_device *hd = svc->hd; 13718c2ecf20Sopenharmony_ci struct gb_module *module, *tmp; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) { 13748c2ecf20Sopenharmony_ci gb_module_del(module); 13758c2ecf20Sopenharmony_ci list_del(&module->hd_node); 13768c2ecf20Sopenharmony_ci gb_module_put(module); 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_civoid gb_svc_del(struct gb_svc *svc) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci gb_connection_disable_rx(svc->connection); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci /* 13858c2ecf20Sopenharmony_ci * The SVC device may have been registered from the request handler. 13868c2ecf20Sopenharmony_ci */ 13878c2ecf20Sopenharmony_ci if (device_is_registered(&svc->dev)) { 13888c2ecf20Sopenharmony_ci gb_svc_debugfs_exit(svc); 13898c2ecf20Sopenharmony_ci gb_svc_watchdog_destroy(svc); 13908c2ecf20Sopenharmony_ci device_del(&svc->dev); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci flush_workqueue(svc->wq); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci gb_svc_remove_modules(svc); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci gb_connection_disable(svc->connection); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_civoid gb_svc_put(struct gb_svc *svc) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci put_device(&svc->dev); 14038c2ecf20Sopenharmony_ci} 1404