18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Greybus connections 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 Google Inc. 68c2ecf20Sopenharmony_ci * Copyright 2014 Linaro Ltd. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 108c2ecf20Sopenharmony_ci#include <linux/greybus.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "greybus_trace.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT 1000 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic void gb_connection_kref_release(struct kref *kref); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(gb_connections_lock); 198c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(gb_connection_mutex); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Caller holds gb_connection_mutex. */ 228c2ecf20Sopenharmony_cistatic bool gb_connection_cport_in_use(struct gb_interface *intf, u16 cport_id) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct gb_host_device *hd = intf->hd; 258c2ecf20Sopenharmony_ci struct gb_connection *connection; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci list_for_each_entry(connection, &hd->connections, hd_links) { 288c2ecf20Sopenharmony_ci if (connection->intf == intf && 298c2ecf20Sopenharmony_ci connection->intf_cport_id == cport_id) 308c2ecf20Sopenharmony_ci return true; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return false; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void gb_connection_get(struct gb_connection *connection) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci kref_get(&connection->kref); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci trace_gb_connection_get(connection); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void gb_connection_put(struct gb_connection *connection) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci trace_gb_connection_put(connection); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci kref_put(&connection->kref, gb_connection_kref_release); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Returns a reference-counted pointer to the connection if found. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic struct gb_connection * 548c2ecf20Sopenharmony_cigb_connection_hd_find(struct gb_host_device *hd, u16 cport_id) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct gb_connection *connection; 578c2ecf20Sopenharmony_ci unsigned long flags; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci spin_lock_irqsave(&gb_connections_lock, flags); 608c2ecf20Sopenharmony_ci list_for_each_entry(connection, &hd->connections, hd_links) 618c2ecf20Sopenharmony_ci if (connection->hd_cport_id == cport_id) { 628c2ecf20Sopenharmony_ci gb_connection_get(connection); 638c2ecf20Sopenharmony_ci goto found; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci connection = NULL; 668c2ecf20Sopenharmony_cifound: 678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gb_connections_lock, flags); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return connection; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Callback from the host driver to let us know that data has been 748c2ecf20Sopenharmony_ci * received on the bundle. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_civoid greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, 778c2ecf20Sopenharmony_ci u8 *data, size_t length) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct gb_connection *connection; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci trace_gb_hd_in(hd); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci connection = gb_connection_hd_find(hd, cport_id); 848c2ecf20Sopenharmony_ci if (!connection) { 858c2ecf20Sopenharmony_ci dev_err(&hd->dev, 868c2ecf20Sopenharmony_ci "nonexistent connection (%zu bytes dropped)\n", length); 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci gb_connection_recv(connection, data, length); 908c2ecf20Sopenharmony_ci gb_connection_put(connection); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(greybus_data_rcvd); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void gb_connection_kref_release(struct kref *kref) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct gb_connection *connection; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci connection = container_of(kref, struct gb_connection, kref); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci trace_gb_connection_release(connection); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci kfree(connection); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void gb_connection_init_name(struct gb_connection *connection) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci u16 hd_cport_id = connection->hd_cport_id; 1088c2ecf20Sopenharmony_ci u16 cport_id = 0; 1098c2ecf20Sopenharmony_ci u8 intf_id = 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (connection->intf) { 1128c2ecf20Sopenharmony_ci intf_id = connection->intf->interface_id; 1138c2ecf20Sopenharmony_ci cport_id = connection->intf_cport_id; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci snprintf(connection->name, sizeof(connection->name), 1178c2ecf20Sopenharmony_ci "%u/%u:%u", hd_cport_id, intf_id, cport_id); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * _gb_connection_create() - create a Greybus connection 1228c2ecf20Sopenharmony_ci * @hd: host device of the connection 1238c2ecf20Sopenharmony_ci * @hd_cport_id: host-device cport id, or -1 for dynamic allocation 1248c2ecf20Sopenharmony_ci * @intf: remote interface, or NULL for static connections 1258c2ecf20Sopenharmony_ci * @bundle: remote-interface bundle (may be NULL) 1268c2ecf20Sopenharmony_ci * @cport_id: remote-interface cport id, or 0 for static connections 1278c2ecf20Sopenharmony_ci * @handler: request handler (may be NULL) 1288c2ecf20Sopenharmony_ci * @flags: connection flags 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Create a Greybus connection, representing the bidirectional link 1318c2ecf20Sopenharmony_ci * between a CPort on a (local) Greybus host device and a CPort on 1328c2ecf20Sopenharmony_ci * another Greybus interface. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * A connection also maintains the state of operations sent over the 1358c2ecf20Sopenharmony_ci * connection. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Serialised against concurrent create and destroy using the 1388c2ecf20Sopenharmony_ci * gb_connection_mutex. 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * Return: A pointer to the new connection if successful, or an ERR_PTR 1418c2ecf20Sopenharmony_ci * otherwise. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic struct gb_connection * 1448c2ecf20Sopenharmony_ci_gb_connection_create(struct gb_host_device *hd, int hd_cport_id, 1458c2ecf20Sopenharmony_ci struct gb_interface *intf, 1468c2ecf20Sopenharmony_ci struct gb_bundle *bundle, int cport_id, 1478c2ecf20Sopenharmony_ci gb_request_handler_t handler, 1488c2ecf20Sopenharmony_ci unsigned long flags) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct gb_connection *connection; 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mutex_lock(&gb_connection_mutex); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (intf && gb_connection_cport_in_use(intf, cport_id)) { 1568c2ecf20Sopenharmony_ci dev_err(&intf->dev, "cport %u already in use\n", cport_id); 1578c2ecf20Sopenharmony_ci ret = -EBUSY; 1588c2ecf20Sopenharmony_ci goto err_unlock; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = gb_hd_cport_allocate(hd, hd_cport_id, flags); 1628c2ecf20Sopenharmony_ci if (ret < 0) { 1638c2ecf20Sopenharmony_ci dev_err(&hd->dev, "failed to allocate cport: %d\n", ret); 1648c2ecf20Sopenharmony_ci goto err_unlock; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci hd_cport_id = ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci connection = kzalloc(sizeof(*connection), GFP_KERNEL); 1698c2ecf20Sopenharmony_ci if (!connection) { 1708c2ecf20Sopenharmony_ci ret = -ENOMEM; 1718c2ecf20Sopenharmony_ci goto err_hd_cport_release; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci connection->hd_cport_id = hd_cport_id; 1758c2ecf20Sopenharmony_ci connection->intf_cport_id = cport_id; 1768c2ecf20Sopenharmony_ci connection->hd = hd; 1778c2ecf20Sopenharmony_ci connection->intf = intf; 1788c2ecf20Sopenharmony_ci connection->bundle = bundle; 1798c2ecf20Sopenharmony_ci connection->handler = handler; 1808c2ecf20Sopenharmony_ci connection->flags = flags; 1818c2ecf20Sopenharmony_ci if (intf && (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES)) 1828c2ecf20Sopenharmony_ci connection->flags |= GB_CONNECTION_FLAG_NO_FLOWCTRL; 1838c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_DISABLED; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci atomic_set(&connection->op_cycle, 0); 1868c2ecf20Sopenharmony_ci mutex_init(&connection->mutex); 1878c2ecf20Sopenharmony_ci spin_lock_init(&connection->lock); 1888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&connection->operations); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1, 1918c2ecf20Sopenharmony_ci dev_name(&hd->dev), hd_cport_id); 1928c2ecf20Sopenharmony_ci if (!connection->wq) { 1938c2ecf20Sopenharmony_ci ret = -ENOMEM; 1948c2ecf20Sopenharmony_ci goto err_free_connection; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci kref_init(&connection->kref); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci gb_connection_init_name(connection); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci spin_lock_irq(&gb_connections_lock); 2028c2ecf20Sopenharmony_ci list_add(&connection->hd_links, &hd->connections); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (bundle) 2058c2ecf20Sopenharmony_ci list_add(&connection->bundle_links, &bundle->connections); 2068c2ecf20Sopenharmony_ci else 2078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&connection->bundle_links); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci spin_unlock_irq(&gb_connections_lock); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci mutex_unlock(&gb_connection_mutex); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci trace_gb_connection_create(connection); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return connection; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cierr_free_connection: 2188c2ecf20Sopenharmony_ci kfree(connection); 2198c2ecf20Sopenharmony_cierr_hd_cport_release: 2208c2ecf20Sopenharmony_ci gb_hd_cport_release(hd, hd_cport_id); 2218c2ecf20Sopenharmony_cierr_unlock: 2228c2ecf20Sopenharmony_ci mutex_unlock(&gb_connection_mutex); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistruct gb_connection * 2288c2ecf20Sopenharmony_cigb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, 2298c2ecf20Sopenharmony_ci gb_request_handler_t handler) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler, 2328c2ecf20Sopenharmony_ci GB_CONNECTION_FLAG_HIGH_PRIO); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistruct gb_connection * 2368c2ecf20Sopenharmony_cigb_connection_create_control(struct gb_interface *intf) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL, 2398c2ecf20Sopenharmony_ci GB_CONNECTION_FLAG_CONTROL | 2408c2ecf20Sopenharmony_ci GB_CONNECTION_FLAG_HIGH_PRIO); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistruct gb_connection * 2448c2ecf20Sopenharmony_cigb_connection_create(struct gb_bundle *bundle, u16 cport_id, 2458c2ecf20Sopenharmony_ci gb_request_handler_t handler) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct gb_interface *intf = bundle->intf; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, 2508c2ecf20Sopenharmony_ci handler, 0); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_create); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistruct gb_connection * 2558c2ecf20Sopenharmony_cigb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, 2568c2ecf20Sopenharmony_ci gb_request_handler_t handler, 2578c2ecf20Sopenharmony_ci unsigned long flags) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct gb_interface *intf = bundle->intf; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(flags & GB_CONNECTION_FLAG_CORE_MASK)) 2628c2ecf20Sopenharmony_ci flags &= ~GB_CONNECTION_FLAG_CORE_MASK; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, 2658c2ecf20Sopenharmony_ci handler, flags); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_create_flags); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistruct gb_connection * 2708c2ecf20Sopenharmony_cigb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id, 2718c2ecf20Sopenharmony_ci unsigned long flags) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci flags |= GB_CONNECTION_FLAG_OFFLOADED; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return gb_connection_create_flags(bundle, cport_id, NULL, flags); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_create_offloaded); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int gb_connection_hd_cport_enable(struct gb_connection *connection) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 2828c2ecf20Sopenharmony_ci int ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (!hd->driver->cport_enable) 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ret = hd->driver->cport_enable(hd, connection->hd_cport_id, 2888c2ecf20Sopenharmony_ci connection->flags); 2898c2ecf20Sopenharmony_ci if (ret) { 2908c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to enable host cport: %d\n", 2918c2ecf20Sopenharmony_ci connection->name, ret); 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void gb_connection_hd_cport_disable(struct gb_connection *connection) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 3018c2ecf20Sopenharmony_ci int ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!hd->driver->cport_disable) 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci ret = hd->driver->cport_disable(hd, connection->hd_cport_id); 3078c2ecf20Sopenharmony_ci if (ret) { 3088c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to disable host cport: %d\n", 3098c2ecf20Sopenharmony_ci connection->name, ret); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int gb_connection_hd_cport_connected(struct gb_connection *connection) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 3168c2ecf20Sopenharmony_ci int ret; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!hd->driver->cport_connected) 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = hd->driver->cport_connected(hd, connection->hd_cport_id); 3228c2ecf20Sopenharmony_ci if (ret) { 3238c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to set connected state: %d\n", 3248c2ecf20Sopenharmony_ci connection->name, ret); 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int gb_connection_hd_cport_flush(struct gb_connection *connection) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 3348c2ecf20Sopenharmony_ci int ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!hd->driver->cport_flush) 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = hd->driver->cport_flush(hd, connection->hd_cport_id); 3408c2ecf20Sopenharmony_ci if (ret) { 3418c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to flush host cport: %d\n", 3428c2ecf20Sopenharmony_ci connection->name, ret); 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int gb_connection_hd_cport_quiesce(struct gb_connection *connection) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 3528c2ecf20Sopenharmony_ci size_t peer_space; 3538c2ecf20Sopenharmony_ci int ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!hd->driver->cport_quiesce) 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci peer_space = sizeof(struct gb_operation_msg_hdr) + 3598c2ecf20Sopenharmony_ci sizeof(struct gb_cport_shutdown_request); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (connection->mode_switch) 3628c2ecf20Sopenharmony_ci peer_space += sizeof(struct gb_operation_msg_hdr); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id, 3658c2ecf20Sopenharmony_ci peer_space, 3668c2ecf20Sopenharmony_ci GB_CONNECTION_CPORT_QUIESCE_TIMEOUT); 3678c2ecf20Sopenharmony_ci if (ret) { 3688c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to quiesce host cport: %d\n", 3698c2ecf20Sopenharmony_ci connection->name, ret); 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int gb_connection_hd_cport_clear(struct gb_connection *connection) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 3798c2ecf20Sopenharmony_ci int ret; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!hd->driver->cport_clear) 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ret = hd->driver->cport_clear(hd, connection->hd_cport_id); 3858c2ecf20Sopenharmony_ci if (ret) { 3868c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to clear host cport: %d\n", 3878c2ecf20Sopenharmony_ci connection->name, ret); 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* 3958c2ecf20Sopenharmony_ci * Request the SVC to create a connection from AP's cport to interface's 3968c2ecf20Sopenharmony_ci * cport. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_cistatic int 3998c2ecf20Sopenharmony_cigb_connection_svc_connection_create(struct gb_connection *connection) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 4028c2ecf20Sopenharmony_ci struct gb_interface *intf; 4038c2ecf20Sopenharmony_ci u8 cport_flags; 4048c2ecf20Sopenharmony_ci int ret; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (gb_connection_is_static(connection)) 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci intf = connection->intf; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * Enable either E2EFC or CSD, unless no flow control is requested. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci cport_flags = GB_SVC_CPORT_FLAG_CSV_N; 4158c2ecf20Sopenharmony_ci if (gb_connection_flow_control_disabled(connection)) { 4168c2ecf20Sopenharmony_ci cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; 4178c2ecf20Sopenharmony_ci } else if (gb_connection_e2efc_enabled(connection)) { 4188c2ecf20Sopenharmony_ci cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | 4198c2ecf20Sopenharmony_ci GB_SVC_CPORT_FLAG_E2EFC; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ret = gb_svc_connection_create(hd->svc, 4238c2ecf20Sopenharmony_ci hd->svc->ap_intf_id, 4248c2ecf20Sopenharmony_ci connection->hd_cport_id, 4258c2ecf20Sopenharmony_ci intf->interface_id, 4268c2ecf20Sopenharmony_ci connection->intf_cport_id, 4278c2ecf20Sopenharmony_ci cport_flags); 4288c2ecf20Sopenharmony_ci if (ret) { 4298c2ecf20Sopenharmony_ci dev_err(&connection->hd->dev, 4308c2ecf20Sopenharmony_ci "%s: failed to create svc connection: %d\n", 4318c2ecf20Sopenharmony_ci connection->name, ret); 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void 4398c2ecf20Sopenharmony_cigb_connection_svc_connection_destroy(struct gb_connection *connection) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci if (gb_connection_is_static(connection)) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci gb_svc_connection_destroy(connection->hd->svc, 4458c2ecf20Sopenharmony_ci connection->hd->svc->ap_intf_id, 4468c2ecf20Sopenharmony_ci connection->hd_cport_id, 4478c2ecf20Sopenharmony_ci connection->intf->interface_id, 4488c2ecf20Sopenharmony_ci connection->intf_cport_id); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* Inform Interface about active CPorts */ 4528c2ecf20Sopenharmony_cistatic int gb_connection_control_connected(struct gb_connection *connection) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct gb_control *control; 4558c2ecf20Sopenharmony_ci u16 cport_id = connection->intf_cport_id; 4568c2ecf20Sopenharmony_ci int ret; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (gb_connection_is_static(connection)) 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (gb_connection_is_control(connection)) 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci control = connection->intf->control; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = gb_control_connected_operation(control, cport_id); 4678c2ecf20Sopenharmony_ci if (ret) { 4688c2ecf20Sopenharmony_ci dev_err(&connection->bundle->dev, 4698c2ecf20Sopenharmony_ci "failed to connect cport: %d\n", ret); 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void 4778c2ecf20Sopenharmony_cigb_connection_control_disconnecting(struct gb_connection *connection) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct gb_control *control; 4808c2ecf20Sopenharmony_ci u16 cport_id = connection->intf_cport_id; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (gb_connection_is_static(connection)) 4848c2ecf20Sopenharmony_ci return; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci control = connection->intf->control; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = gb_control_disconnecting_operation(control, cport_id); 4898c2ecf20Sopenharmony_ci if (ret) { 4908c2ecf20Sopenharmony_ci dev_err(&connection->hd->dev, 4918c2ecf20Sopenharmony_ci "%s: failed to send disconnecting: %d\n", 4928c2ecf20Sopenharmony_ci connection->name, ret); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void 4978c2ecf20Sopenharmony_cigb_connection_control_disconnected(struct gb_connection *connection) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct gb_control *control; 5008c2ecf20Sopenharmony_ci u16 cport_id = connection->intf_cport_id; 5018c2ecf20Sopenharmony_ci int ret; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (gb_connection_is_static(connection)) 5048c2ecf20Sopenharmony_ci return; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci control = connection->intf->control; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (gb_connection_is_control(connection)) { 5098c2ecf20Sopenharmony_ci if (connection->mode_switch) { 5108c2ecf20Sopenharmony_ci ret = gb_control_mode_switch_operation(control); 5118c2ecf20Sopenharmony_ci if (ret) { 5128c2ecf20Sopenharmony_ci /* 5138c2ecf20Sopenharmony_ci * Allow mode switch to time out waiting for 5148c2ecf20Sopenharmony_ci * mailbox event. 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci return; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci ret = gb_control_disconnected_operation(control, cport_id); 5248c2ecf20Sopenharmony_ci if (ret) { 5258c2ecf20Sopenharmony_ci dev_warn(&connection->bundle->dev, 5268c2ecf20Sopenharmony_ci "failed to disconnect cport: %d\n", ret); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int gb_connection_shutdown_operation(struct gb_connection *connection, 5318c2ecf20Sopenharmony_ci u8 phase) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct gb_cport_shutdown_request *req; 5348c2ecf20Sopenharmony_ci struct gb_operation *operation; 5358c2ecf20Sopenharmony_ci int ret; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci operation = gb_operation_create_core(connection, 5388c2ecf20Sopenharmony_ci GB_REQUEST_TYPE_CPORT_SHUTDOWN, 5398c2ecf20Sopenharmony_ci sizeof(*req), 0, 0, 5408c2ecf20Sopenharmony_ci GFP_KERNEL); 5418c2ecf20Sopenharmony_ci if (!operation) 5428c2ecf20Sopenharmony_ci return -ENOMEM; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci req = operation->request->payload; 5458c2ecf20Sopenharmony_ci req->phase = phase; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci ret = gb_operation_request_send_sync(operation); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci gb_operation_put(operation); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return ret; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int gb_connection_cport_shutdown(struct gb_connection *connection, 5558c2ecf20Sopenharmony_ci u8 phase) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 5588c2ecf20Sopenharmony_ci const struct gb_hd_driver *drv = hd->driver; 5598c2ecf20Sopenharmony_ci int ret; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (gb_connection_is_static(connection)) 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (gb_connection_is_offloaded(connection)) { 5658c2ecf20Sopenharmony_ci if (!drv->cport_shutdown) 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = drv->cport_shutdown(hd, connection->hd_cport_id, phase, 5698c2ecf20Sopenharmony_ci GB_OPERATION_TIMEOUT_DEFAULT); 5708c2ecf20Sopenharmony_ci } else { 5718c2ecf20Sopenharmony_ci ret = gb_connection_shutdown_operation(connection, phase); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (ret) { 5758c2ecf20Sopenharmony_ci dev_err(&hd->dev, "%s: failed to send cport shutdown (phase %d): %d\n", 5768c2ecf20Sopenharmony_ci connection->name, phase, ret); 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int 5848c2ecf20Sopenharmony_cigb_connection_cport_shutdown_phase_1(struct gb_connection *connection) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci return gb_connection_cport_shutdown(connection, 1); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int 5908c2ecf20Sopenharmony_cigb_connection_cport_shutdown_phase_2(struct gb_connection *connection) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci return gb_connection_cport_shutdown(connection, 2); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/* 5968c2ecf20Sopenharmony_ci * Cancel all active operations on a connection. 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * Locking: Called with connection lock held and state set to DISABLED or 5998c2ecf20Sopenharmony_ci * DISCONNECTING. 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_cistatic void gb_connection_cancel_operations(struct gb_connection *connection, 6028c2ecf20Sopenharmony_ci int errno) 6038c2ecf20Sopenharmony_ci __must_hold(&connection->lock) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct gb_operation *operation; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci while (!list_empty(&connection->operations)) { 6088c2ecf20Sopenharmony_ci operation = list_last_entry(&connection->operations, 6098c2ecf20Sopenharmony_ci struct gb_operation, links); 6108c2ecf20Sopenharmony_ci gb_operation_get(operation); 6118c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (gb_operation_is_incoming(operation)) 6148c2ecf20Sopenharmony_ci gb_operation_cancel_incoming(operation, errno); 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci gb_operation_cancel(operation, errno); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci gb_operation_put(operation); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci/* 6258c2ecf20Sopenharmony_ci * Cancel all active incoming operations on a connection. 6268c2ecf20Sopenharmony_ci * 6278c2ecf20Sopenharmony_ci * Locking: Called with connection lock held and state set to ENABLED_TX. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_cistatic void 6308c2ecf20Sopenharmony_cigb_connection_flush_incoming_operations(struct gb_connection *connection, 6318c2ecf20Sopenharmony_ci int errno) 6328c2ecf20Sopenharmony_ci __must_hold(&connection->lock) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct gb_operation *operation; 6358c2ecf20Sopenharmony_ci bool incoming; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci while (!list_empty(&connection->operations)) { 6388c2ecf20Sopenharmony_ci incoming = false; 6398c2ecf20Sopenharmony_ci list_for_each_entry(operation, &connection->operations, 6408c2ecf20Sopenharmony_ci links) { 6418c2ecf20Sopenharmony_ci if (gb_operation_is_incoming(operation)) { 6428c2ecf20Sopenharmony_ci gb_operation_get(operation); 6438c2ecf20Sopenharmony_ci incoming = true; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (!incoming) 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* FIXME: flush, not cancel? */ 6548c2ecf20Sopenharmony_ci gb_operation_cancel_incoming(operation, errno); 6558c2ecf20Sopenharmony_ci gb_operation_put(operation); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/* 6628c2ecf20Sopenharmony_ci * _gb_connection_enable() - enable a connection 6638c2ecf20Sopenharmony_ci * @connection: connection to enable 6648c2ecf20Sopenharmony_ci * @rx: whether to enable incoming requests 6658c2ecf20Sopenharmony_ci * 6668c2ecf20Sopenharmony_ci * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and 6678c2ecf20Sopenharmony_ci * ENABLED_TX->ENABLED state transitions. 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * Locking: Caller holds connection->mutex. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic int _gb_connection_enable(struct gb_connection *connection, bool rx) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci int ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* Handle ENABLED_TX -> ENABLED transitions. */ 6768c2ecf20Sopenharmony_ci if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { 6778c2ecf20Sopenharmony_ci if (!(connection->handler && rx)) 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 6818c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_ENABLED; 6828c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return 0; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = gb_connection_hd_cport_enable(connection); 6888c2ecf20Sopenharmony_ci if (ret) 6898c2ecf20Sopenharmony_ci return ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = gb_connection_svc_connection_create(connection); 6928c2ecf20Sopenharmony_ci if (ret) 6938c2ecf20Sopenharmony_ci goto err_hd_cport_clear; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ret = gb_connection_hd_cport_connected(connection); 6968c2ecf20Sopenharmony_ci if (ret) 6978c2ecf20Sopenharmony_ci goto err_svc_connection_destroy; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 7008c2ecf20Sopenharmony_ci if (connection->handler && rx) 7018c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_ENABLED; 7028c2ecf20Sopenharmony_ci else 7038c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_ENABLED_TX; 7048c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ret = gb_connection_control_connected(connection); 7078c2ecf20Sopenharmony_ci if (ret) 7088c2ecf20Sopenharmony_ci goto err_control_disconnecting; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cierr_control_disconnecting: 7138c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 7148c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_DISCONNECTING; 7158c2ecf20Sopenharmony_ci gb_connection_cancel_operations(connection, -ESHUTDOWN); 7168c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Transmit queue should already be empty. */ 7198c2ecf20Sopenharmony_ci gb_connection_hd_cport_flush(connection); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci gb_connection_control_disconnecting(connection); 7228c2ecf20Sopenharmony_ci gb_connection_cport_shutdown_phase_1(connection); 7238c2ecf20Sopenharmony_ci gb_connection_hd_cport_quiesce(connection); 7248c2ecf20Sopenharmony_ci gb_connection_cport_shutdown_phase_2(connection); 7258c2ecf20Sopenharmony_ci gb_connection_control_disconnected(connection); 7268c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_DISABLED; 7278c2ecf20Sopenharmony_cierr_svc_connection_destroy: 7288c2ecf20Sopenharmony_ci gb_connection_svc_connection_destroy(connection); 7298c2ecf20Sopenharmony_cierr_hd_cport_clear: 7308c2ecf20Sopenharmony_ci gb_connection_hd_cport_clear(connection); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci gb_connection_hd_cport_disable(connection); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return ret; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ciint gb_connection_enable(struct gb_connection *connection) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci int ret = 0; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci mutex_lock(&connection->mutex); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (connection->state == GB_CONNECTION_STATE_ENABLED) 7448c2ecf20Sopenharmony_ci goto out_unlock; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci ret = _gb_connection_enable(connection, true); 7478c2ecf20Sopenharmony_ci if (!ret) 7488c2ecf20Sopenharmony_ci trace_gb_connection_enable(connection); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ciout_unlock: 7518c2ecf20Sopenharmony_ci mutex_unlock(&connection->mutex); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_enable); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ciint gb_connection_enable_tx(struct gb_connection *connection) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci int ret = 0; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci mutex_lock(&connection->mutex); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (connection->state == GB_CONNECTION_STATE_ENABLED) { 7648c2ecf20Sopenharmony_ci ret = -EINVAL; 7658c2ecf20Sopenharmony_ci goto out_unlock; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) 7698c2ecf20Sopenharmony_ci goto out_unlock; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ret = _gb_connection_enable(connection, false); 7728c2ecf20Sopenharmony_ci if (!ret) 7738c2ecf20Sopenharmony_ci trace_gb_connection_enable(connection); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ciout_unlock: 7768c2ecf20Sopenharmony_ci mutex_unlock(&connection->mutex); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return ret; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_enable_tx); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_civoid gb_connection_disable_rx(struct gb_connection *connection) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci mutex_lock(&connection->mutex); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 7878c2ecf20Sopenharmony_ci if (connection->state != GB_CONNECTION_STATE_ENABLED) { 7888c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 7898c2ecf20Sopenharmony_ci goto out_unlock; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_ENABLED_TX; 7928c2ecf20Sopenharmony_ci gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); 7938c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci trace_gb_connection_disable(connection); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciout_unlock: 7988c2ecf20Sopenharmony_ci mutex_unlock(&connection->mutex); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_disable_rx); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_civoid gb_connection_mode_switch_prepare(struct gb_connection *connection) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci connection->mode_switch = true; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_civoid gb_connection_mode_switch_complete(struct gb_connection *connection) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci gb_connection_svc_connection_destroy(connection); 8108c2ecf20Sopenharmony_ci gb_connection_hd_cport_clear(connection); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci gb_connection_hd_cport_disable(connection); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci connection->mode_switch = false; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_civoid gb_connection_disable(struct gb_connection *connection) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci mutex_lock(&connection->mutex); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (connection->state == GB_CONNECTION_STATE_DISABLED) 8228c2ecf20Sopenharmony_ci goto out_unlock; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci trace_gb_connection_disable(connection); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 8278c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_DISCONNECTING; 8288c2ecf20Sopenharmony_ci gb_connection_cancel_operations(connection, -ESHUTDOWN); 8298c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci gb_connection_hd_cport_flush(connection); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci gb_connection_control_disconnecting(connection); 8348c2ecf20Sopenharmony_ci gb_connection_cport_shutdown_phase_1(connection); 8358c2ecf20Sopenharmony_ci gb_connection_hd_cport_quiesce(connection); 8368c2ecf20Sopenharmony_ci gb_connection_cport_shutdown_phase_2(connection); 8378c2ecf20Sopenharmony_ci gb_connection_control_disconnected(connection); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_DISABLED; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* control-connection tear down is deferred when mode switching */ 8428c2ecf20Sopenharmony_ci if (!connection->mode_switch) { 8438c2ecf20Sopenharmony_ci gb_connection_svc_connection_destroy(connection); 8448c2ecf20Sopenharmony_ci gb_connection_hd_cport_clear(connection); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci gb_connection_hd_cport_disable(connection); 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ciout_unlock: 8508c2ecf20Sopenharmony_ci mutex_unlock(&connection->mutex); 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_disable); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci/* Disable a connection without communicating with the remote end. */ 8558c2ecf20Sopenharmony_civoid gb_connection_disable_forced(struct gb_connection *connection) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci mutex_lock(&connection->mutex); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (connection->state == GB_CONNECTION_STATE_DISABLED) 8608c2ecf20Sopenharmony_ci goto out_unlock; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci trace_gb_connection_disable(connection); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci spin_lock_irq(&connection->lock); 8658c2ecf20Sopenharmony_ci connection->state = GB_CONNECTION_STATE_DISABLED; 8668c2ecf20Sopenharmony_ci gb_connection_cancel_operations(connection, -ESHUTDOWN); 8678c2ecf20Sopenharmony_ci spin_unlock_irq(&connection->lock); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci gb_connection_hd_cport_flush(connection); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci gb_connection_svc_connection_destroy(connection); 8728c2ecf20Sopenharmony_ci gb_connection_hd_cport_clear(connection); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci gb_connection_hd_cport_disable(connection); 8758c2ecf20Sopenharmony_ciout_unlock: 8768c2ecf20Sopenharmony_ci mutex_unlock(&connection->mutex); 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_disable_forced); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/* Caller must have disabled the connection before destroying it. */ 8818c2ecf20Sopenharmony_civoid gb_connection_destroy(struct gb_connection *connection) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci if (!connection) 8848c2ecf20Sopenharmony_ci return; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) 8878c2ecf20Sopenharmony_ci gb_connection_disable(connection); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci mutex_lock(&gb_connection_mutex); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci spin_lock_irq(&gb_connections_lock); 8928c2ecf20Sopenharmony_ci list_del(&connection->bundle_links); 8938c2ecf20Sopenharmony_ci list_del(&connection->hd_links); 8948c2ecf20Sopenharmony_ci spin_unlock_irq(&gb_connections_lock); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci destroy_workqueue(connection->wq); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci gb_hd_cport_release(connection->hd, connection->hd_cport_id); 8998c2ecf20Sopenharmony_ci connection->hd_cport_id = CPORT_ID_BAD; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci mutex_unlock(&gb_connection_mutex); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci gb_connection_put(connection); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_destroy); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_civoid gb_connection_latency_tag_enable(struct gb_connection *connection) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 9108c2ecf20Sopenharmony_ci int ret; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (!hd->driver->latency_tag_enable) 9138c2ecf20Sopenharmony_ci return; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id); 9168c2ecf20Sopenharmony_ci if (ret) { 9178c2ecf20Sopenharmony_ci dev_err(&connection->hd->dev, 9188c2ecf20Sopenharmony_ci "%s: failed to enable latency tag: %d\n", 9198c2ecf20Sopenharmony_ci connection->name, ret); 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_civoid gb_connection_latency_tag_disable(struct gb_connection *connection) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct gb_host_device *hd = connection->hd; 9278c2ecf20Sopenharmony_ci int ret; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (!hd->driver->latency_tag_disable) 9308c2ecf20Sopenharmony_ci return; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id); 9338c2ecf20Sopenharmony_ci if (ret) { 9348c2ecf20Sopenharmony_ci dev_err(&connection->hd->dev, 9358c2ecf20Sopenharmony_ci "%s: failed to disable latency tag: %d\n", 9368c2ecf20Sopenharmony_ci connection->name, ret); 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); 940