18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Shared Memory Communications Direct over ISM devices (SMC-D) 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Functions for ISM device. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2018 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci#include <linux/mutex.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <asm/page.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "smc.h" 158c2ecf20Sopenharmony_ci#include "smc_core.h" 168c2ecf20Sopenharmony_ci#include "smc_ism.h" 178c2ecf20Sopenharmony_ci#include "smc_pnet.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct smcd_dev_list smcd_dev_list = { 208c2ecf20Sopenharmony_ci .list = LIST_HEAD_INIT(smcd_dev_list.list), 218c2ecf20Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(smcd_dev_list.mutex) 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cibool smc_ism_v2_capable; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Test if an ISM communication is possible - same CPC */ 278c2ecf20Sopenharmony_ciint smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci return smcd->ops->query_remote_gid(smcd, peer_gid, vlan_id ? 1 : 0, 308c2ecf20Sopenharmony_ci vlan_id); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciint smc_ism_write(struct smcd_dev *smcd, const struct smc_ism_position *pos, 348c2ecf20Sopenharmony_ci void *data, size_t len) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci int rc; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci rc = smcd->ops->move_data(smcd, pos->token, pos->index, pos->signal, 398c2ecf20Sopenharmony_ci pos->offset, data, len); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return rc < 0 ? rc : 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_civoid smc_ism_get_system_eid(struct smcd_dev *smcd, u8 **eid) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci smcd->ops->get_system_eid(smcd, eid); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciu16 smc_ism_get_chid(struct smcd_dev *smcd) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return smcd->ops->get_chid(smcd); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Set a connection using this DMBE. */ 558c2ecf20Sopenharmony_civoid smc_ism_set_conn(struct smc_connection *conn) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci unsigned long flags; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->lgr->smcd->lock, flags); 608c2ecf20Sopenharmony_ci conn->lgr->smcd->conn[conn->rmb_desc->sba_idx] = conn; 618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->lgr->smcd->lock, flags); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Unset a connection using this DMBE. */ 658c2ecf20Sopenharmony_civoid smc_ism_unset_conn(struct smc_connection *conn) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned long flags; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!conn->rmb_desc) 708c2ecf20Sopenharmony_ci return; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->lgr->smcd->lock, flags); 738c2ecf20Sopenharmony_ci conn->lgr->smcd->conn[conn->rmb_desc->sba_idx] = NULL; 748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->lgr->smcd->lock, flags); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Register a VLAN identifier with the ISM device. Use a reference count 788c2ecf20Sopenharmony_ci * and add a VLAN identifier only when the first DMB using this VLAN is 798c2ecf20Sopenharmony_ci * registered. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ciint smc_ism_get_vlan(struct smcd_dev *smcd, unsigned short vlanid) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct smc_ism_vlanid *new_vlan, *vlan; 848c2ecf20Sopenharmony_ci unsigned long flags; 858c2ecf20Sopenharmony_ci int rc = 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!vlanid) /* No valid vlan id */ 888c2ecf20Sopenharmony_ci return -EINVAL; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* create new vlan entry, in case we need it */ 918c2ecf20Sopenharmony_ci new_vlan = kzalloc(sizeof(*new_vlan), GFP_KERNEL); 928c2ecf20Sopenharmony_ci if (!new_vlan) 938c2ecf20Sopenharmony_ci return -ENOMEM; 948c2ecf20Sopenharmony_ci new_vlan->vlanid = vlanid; 958c2ecf20Sopenharmony_ci refcount_set(&new_vlan->refcnt, 1); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* if there is an existing entry, increase count and return */ 988c2ecf20Sopenharmony_ci spin_lock_irqsave(&smcd->lock, flags); 998c2ecf20Sopenharmony_ci list_for_each_entry(vlan, &smcd->vlan, list) { 1008c2ecf20Sopenharmony_ci if (vlan->vlanid == vlanid) { 1018c2ecf20Sopenharmony_ci refcount_inc(&vlan->refcnt); 1028c2ecf20Sopenharmony_ci kfree(new_vlan); 1038c2ecf20Sopenharmony_ci goto out; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* no existing entry found. 1088c2ecf20Sopenharmony_ci * add new entry to device; might fail, e.g., if HW limit reached 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci if (smcd->ops->add_vlan_id(smcd, vlanid)) { 1118c2ecf20Sopenharmony_ci kfree(new_vlan); 1128c2ecf20Sopenharmony_ci rc = -EIO; 1138c2ecf20Sopenharmony_ci goto out; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci list_add_tail(&new_vlan->list, &smcd->vlan); 1168c2ecf20Sopenharmony_ciout: 1178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smcd->lock, flags); 1188c2ecf20Sopenharmony_ci return rc; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Unregister a VLAN identifier with the ISM device. Use a reference count 1228c2ecf20Sopenharmony_ci * and remove a VLAN identifier only when the last DMB using this VLAN is 1238c2ecf20Sopenharmony_ci * unregistered. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ciint smc_ism_put_vlan(struct smcd_dev *smcd, unsigned short vlanid) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct smc_ism_vlanid *vlan; 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci bool found = false; 1308c2ecf20Sopenharmony_ci int rc = 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!vlanid) /* No valid vlan id */ 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_lock_irqsave(&smcd->lock, flags); 1368c2ecf20Sopenharmony_ci list_for_each_entry(vlan, &smcd->vlan, list) { 1378c2ecf20Sopenharmony_ci if (vlan->vlanid == vlanid) { 1388c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&vlan->refcnt)) 1398c2ecf20Sopenharmony_ci goto out; 1408c2ecf20Sopenharmony_ci found = true; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci if (!found) { 1458c2ecf20Sopenharmony_ci rc = -ENOENT; 1468c2ecf20Sopenharmony_ci goto out; /* VLAN id not in table */ 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Found and the last reference just gone */ 1508c2ecf20Sopenharmony_ci if (smcd->ops->del_vlan_id(smcd, vlanid)) 1518c2ecf20Sopenharmony_ci rc = -EIO; 1528c2ecf20Sopenharmony_ci list_del(&vlan->list); 1538c2ecf20Sopenharmony_ci kfree(vlan); 1548c2ecf20Sopenharmony_ciout: 1558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smcd->lock, flags); 1568c2ecf20Sopenharmony_ci return rc; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciint smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct smcd_dmb dmb; 1628c2ecf20Sopenharmony_ci int rc = 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!dmb_desc->dma_addr) 1658c2ecf20Sopenharmony_ci return rc; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci memset(&dmb, 0, sizeof(dmb)); 1688c2ecf20Sopenharmony_ci dmb.dmb_tok = dmb_desc->token; 1698c2ecf20Sopenharmony_ci dmb.sba_idx = dmb_desc->sba_idx; 1708c2ecf20Sopenharmony_ci dmb.cpu_addr = dmb_desc->cpu_addr; 1718c2ecf20Sopenharmony_ci dmb.dma_addr = dmb_desc->dma_addr; 1728c2ecf20Sopenharmony_ci dmb.dmb_len = dmb_desc->len; 1738c2ecf20Sopenharmony_ci rc = smcd->ops->unregister_dmb(smcd, &dmb); 1748c2ecf20Sopenharmony_ci if (!rc || rc == ISM_ERROR) { 1758c2ecf20Sopenharmony_ci dmb_desc->cpu_addr = NULL; 1768c2ecf20Sopenharmony_ci dmb_desc->dma_addr = 0; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return rc; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciint smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len, 1838c2ecf20Sopenharmony_ci struct smc_buf_desc *dmb_desc) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct smcd_dmb dmb; 1868c2ecf20Sopenharmony_ci int rc; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci memset(&dmb, 0, sizeof(dmb)); 1898c2ecf20Sopenharmony_ci dmb.dmb_len = dmb_len; 1908c2ecf20Sopenharmony_ci dmb.sba_idx = dmb_desc->sba_idx; 1918c2ecf20Sopenharmony_ci dmb.vlan_id = lgr->vlan_id; 1928c2ecf20Sopenharmony_ci dmb.rgid = lgr->peer_gid; 1938c2ecf20Sopenharmony_ci rc = lgr->smcd->ops->register_dmb(lgr->smcd, &dmb); 1948c2ecf20Sopenharmony_ci if (!rc) { 1958c2ecf20Sopenharmony_ci dmb_desc->sba_idx = dmb.sba_idx; 1968c2ecf20Sopenharmony_ci dmb_desc->token = dmb.dmb_tok; 1978c2ecf20Sopenharmony_ci dmb_desc->cpu_addr = dmb.cpu_addr; 1988c2ecf20Sopenharmony_ci dmb_desc->dma_addr = dmb.dma_addr; 1998c2ecf20Sopenharmony_ci dmb_desc->len = dmb.dmb_len; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci return rc; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistruct smc_ism_event_work { 2058c2ecf20Sopenharmony_ci struct work_struct work; 2068c2ecf20Sopenharmony_ci struct smcd_dev *smcd; 2078c2ecf20Sopenharmony_ci struct smcd_event event; 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define ISM_EVENT_REQUEST 0x0001 2118c2ecf20Sopenharmony_ci#define ISM_EVENT_RESPONSE 0x0002 2128c2ecf20Sopenharmony_ci#define ISM_EVENT_REQUEST_IR 0x00000001 2138c2ecf20Sopenharmony_ci#define ISM_EVENT_CODE_SHUTDOWN 0x80 2148c2ecf20Sopenharmony_ci#define ISM_EVENT_CODE_TESTLINK 0x83 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciunion smcd_sw_event_info { 2178c2ecf20Sopenharmony_ci u64 info; 2188c2ecf20Sopenharmony_ci struct { 2198c2ecf20Sopenharmony_ci u8 uid[SMC_LGR_ID_SIZE]; 2208c2ecf20Sopenharmony_ci unsigned short vlan_id; 2218c2ecf20Sopenharmony_ci u16 code; 2228c2ecf20Sopenharmony_ci }; 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void smcd_handle_sw_event(struct smc_ism_event_work *wrk) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci union smcd_sw_event_info ev_info; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ev_info.info = wrk->event.info; 2308c2ecf20Sopenharmony_ci switch (wrk->event.code) { 2318c2ecf20Sopenharmony_ci case ISM_EVENT_CODE_SHUTDOWN: /* Peer shut down DMBs */ 2328c2ecf20Sopenharmony_ci smc_smcd_terminate(wrk->smcd, wrk->event.tok, ev_info.vlan_id); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case ISM_EVENT_CODE_TESTLINK: /* Activity timer */ 2358c2ecf20Sopenharmony_ci if (ev_info.code == ISM_EVENT_REQUEST) { 2368c2ecf20Sopenharmony_ci ev_info.code = ISM_EVENT_RESPONSE; 2378c2ecf20Sopenharmony_ci wrk->smcd->ops->signal_event(wrk->smcd, 2388c2ecf20Sopenharmony_ci wrk->event.tok, 2398c2ecf20Sopenharmony_ci ISM_EVENT_REQUEST_IR, 2408c2ecf20Sopenharmony_ci ISM_EVENT_CODE_TESTLINK, 2418c2ecf20Sopenharmony_ci ev_info.info); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciint smc_ism_signal_shutdown(struct smc_link_group *lgr) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci int rc; 2508c2ecf20Sopenharmony_ci union smcd_sw_event_info ev_info; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (lgr->peer_shutdown) 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci memcpy(ev_info.uid, lgr->id, SMC_LGR_ID_SIZE); 2568c2ecf20Sopenharmony_ci ev_info.vlan_id = lgr->vlan_id; 2578c2ecf20Sopenharmony_ci ev_info.code = ISM_EVENT_REQUEST; 2588c2ecf20Sopenharmony_ci rc = lgr->smcd->ops->signal_event(lgr->smcd, lgr->peer_gid, 2598c2ecf20Sopenharmony_ci ISM_EVENT_REQUEST_IR, 2608c2ecf20Sopenharmony_ci ISM_EVENT_CODE_SHUTDOWN, 2618c2ecf20Sopenharmony_ci ev_info.info); 2628c2ecf20Sopenharmony_ci return rc; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* worker for SMC-D events */ 2668c2ecf20Sopenharmony_cistatic void smc_ism_event_work(struct work_struct *work) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct smc_ism_event_work *wrk = 2698c2ecf20Sopenharmony_ci container_of(work, struct smc_ism_event_work, work); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci switch (wrk->event.type) { 2728c2ecf20Sopenharmony_ci case ISM_EVENT_GID: /* GID event, token is peer GID */ 2738c2ecf20Sopenharmony_ci smc_smcd_terminate(wrk->smcd, wrk->event.tok, VLAN_VID_MASK); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case ISM_EVENT_DMB: 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci case ISM_EVENT_SWR: /* Software defined event */ 2788c2ecf20Sopenharmony_ci smcd_handle_sw_event(wrk); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci kfree(wrk); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void smcd_release(struct device *dev) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct smcd_dev *smcd = container_of(dev, struct smcd_dev, dev); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci kfree(smcd->conn); 2898c2ecf20Sopenharmony_ci kfree(smcd); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistruct smcd_dev *smcd_alloc_dev(struct device *parent, const char *name, 2938c2ecf20Sopenharmony_ci const struct smcd_ops *ops, int max_dmbs) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct smcd_dev *smcd; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci smcd = kzalloc(sizeof(*smcd), GFP_KERNEL); 2988c2ecf20Sopenharmony_ci if (!smcd) 2998c2ecf20Sopenharmony_ci return NULL; 3008c2ecf20Sopenharmony_ci smcd->conn = kcalloc(max_dmbs, sizeof(struct smc_connection *), 3018c2ecf20Sopenharmony_ci GFP_KERNEL); 3028c2ecf20Sopenharmony_ci if (!smcd->conn) { 3038c2ecf20Sopenharmony_ci kfree(smcd); 3048c2ecf20Sopenharmony_ci return NULL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)", 3088c2ecf20Sopenharmony_ci WQ_MEM_RECLAIM, name); 3098c2ecf20Sopenharmony_ci if (!smcd->event_wq) { 3108c2ecf20Sopenharmony_ci kfree(smcd->conn); 3118c2ecf20Sopenharmony_ci kfree(smcd); 3128c2ecf20Sopenharmony_ci return NULL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci smcd->dev.parent = parent; 3168c2ecf20Sopenharmony_ci smcd->dev.release = smcd_release; 3178c2ecf20Sopenharmony_ci device_initialize(&smcd->dev); 3188c2ecf20Sopenharmony_ci dev_set_name(&smcd->dev, name); 3198c2ecf20Sopenharmony_ci smcd->ops = ops; 3208c2ecf20Sopenharmony_ci if (smc_pnetid_by_dev_port(parent, 0, smcd->pnetid)) 3218c2ecf20Sopenharmony_ci smc_pnetid_by_table_smcd(smcd); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci spin_lock_init(&smcd->lock); 3248c2ecf20Sopenharmony_ci spin_lock_init(&smcd->lgr_lock); 3258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&smcd->vlan); 3268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&smcd->lgr_list); 3278c2ecf20Sopenharmony_ci init_waitqueue_head(&smcd->lgrs_deleted); 3288c2ecf20Sopenharmony_ci return smcd; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smcd_alloc_dev); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ciint smcd_register_dev(struct smcd_dev *smcd) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int rc; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 3378c2ecf20Sopenharmony_ci if (list_empty(&smcd_dev_list.list)) { 3388c2ecf20Sopenharmony_ci u8 *system_eid = NULL; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci smc_ism_get_system_eid(smcd, &system_eid); 3418c2ecf20Sopenharmony_ci if (system_eid[24] != '0' || system_eid[28] != '0') 3428c2ecf20Sopenharmony_ci smc_ism_v2_capable = true; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci /* sort list: devices without pnetid before devices with pnetid */ 3458c2ecf20Sopenharmony_ci if (smcd->pnetid[0]) 3468c2ecf20Sopenharmony_ci list_add_tail(&smcd->list, &smcd_dev_list.list); 3478c2ecf20Sopenharmony_ci else 3488c2ecf20Sopenharmony_ci list_add(&smcd->list, &smcd_dev_list.list); 3498c2ecf20Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n", 3528c2ecf20Sopenharmony_ci dev_name(&smcd->dev), smcd->pnetid, 3538c2ecf20Sopenharmony_ci smcd->pnetid_by_user ? " (user defined)" : ""); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rc = device_add(&smcd->dev); 3568c2ecf20Sopenharmony_ci if (rc) { 3578c2ecf20Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 3588c2ecf20Sopenharmony_ci list_del(&smcd->list); 3598c2ecf20Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return rc; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smcd_register_dev); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_civoid smcd_unregister_dev(struct smcd_dev *smcd) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci pr_warn_ratelimited("smc: removing smcd device %s\n", 3698c2ecf20Sopenharmony_ci dev_name(&smcd->dev)); 3708c2ecf20Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 3718c2ecf20Sopenharmony_ci list_del_init(&smcd->list); 3728c2ecf20Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 3738c2ecf20Sopenharmony_ci smcd->going_away = 1; 3748c2ecf20Sopenharmony_ci smc_smcd_terminate_all(smcd); 3758c2ecf20Sopenharmony_ci flush_workqueue(smcd->event_wq); 3768c2ecf20Sopenharmony_ci destroy_workqueue(smcd->event_wq); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci device_del(&smcd->dev); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smcd_unregister_dev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_civoid smcd_free_dev(struct smcd_dev *smcd) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci put_device(&smcd->dev); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smcd_free_dev); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/* SMCD Device event handler. Called from ISM device interrupt handler. 3898c2ecf20Sopenharmony_ci * Parameters are smcd device pointer, 3908c2ecf20Sopenharmony_ci * - event->type (0 --> DMB, 1 --> GID), 3918c2ecf20Sopenharmony_ci * - event->code (event code), 3928c2ecf20Sopenharmony_ci * - event->tok (either DMB token when event type 0, or GID when event type 1) 3938c2ecf20Sopenharmony_ci * - event->time (time of day) 3948c2ecf20Sopenharmony_ci * - event->info (debug info). 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * Context: 3978c2ecf20Sopenharmony_ci * - Function called in IRQ context from ISM device driver event handler. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_civoid smcd_handle_event(struct smcd_dev *smcd, struct smcd_event *event) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct smc_ism_event_work *wrk; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (smcd->going_away) 4048c2ecf20Sopenharmony_ci return; 4058c2ecf20Sopenharmony_ci /* copy event to event work queue, and let it be handled there */ 4068c2ecf20Sopenharmony_ci wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); 4078c2ecf20Sopenharmony_ci if (!wrk) 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci INIT_WORK(&wrk->work, smc_ism_event_work); 4108c2ecf20Sopenharmony_ci wrk->smcd = smcd; 4118c2ecf20Sopenharmony_ci wrk->event = *event; 4128c2ecf20Sopenharmony_ci queue_work(smcd->event_wq, &wrk->work); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smcd_handle_event); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* SMCD Device interrupt handler. Called from ISM device interrupt handler. 4178c2ecf20Sopenharmony_ci * Parameters are smcd device pointer and DMB number. Find the connection and 4188c2ecf20Sopenharmony_ci * schedule the tasklet for this connection. 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Context: 4218c2ecf20Sopenharmony_ci * - Function called in IRQ context from ISM device driver IRQ handler. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_civoid smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct smc_connection *conn = NULL; 4268c2ecf20Sopenharmony_ci unsigned long flags; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci spin_lock_irqsave(&smcd->lock, flags); 4298c2ecf20Sopenharmony_ci conn = smcd->conn[dmbno]; 4308c2ecf20Sopenharmony_ci if (conn && !conn->killed) 4318c2ecf20Sopenharmony_ci tasklet_schedule(&conn->rx_tsklet); 4328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smcd->lock, flags); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smcd_handle_irq); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_civoid __init smc_ism_init(void) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci smc_ism_v2_capable = false; 4398c2ecf20Sopenharmony_ci} 440