162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 362306a36Sopenharmony_ci * redistributing this file, you may do so under either license. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * GPL LICENSE SUMMARY 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright(c) 2012 Intel Corporation. All rights reserved. 862306a36Sopenharmony_ci * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1162306a36Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 1262306a36Sopenharmony_ci * published by the Free Software Foundation. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * BSD LICENSE 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Copyright(c) 2012 Intel Corporation. All rights reserved. 1762306a36Sopenharmony_ci * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 2062306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 2162306a36Sopenharmony_ci * are met: 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 2462306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 2562306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copy 2662306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 2762306a36Sopenharmony_ci * the documentation and/or other materials provided with the 2862306a36Sopenharmony_ci * distribution. 2962306a36Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 3062306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived 3162306a36Sopenharmony_ci * from this software without specific prior written permission. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3462306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3562306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3662306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3762306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3862306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3962306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 4062306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 4162306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 4262306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 4362306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * PCIe NTB Transport Linux driver 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Contact Information: 4862306a36Sopenharmony_ci * Jon Mason <jon.mason@intel.com> 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#include <linux/debugfs.h> 5162306a36Sopenharmony_ci#include <linux/delay.h> 5262306a36Sopenharmony_ci#include <linux/dmaengine.h> 5362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 5462306a36Sopenharmony_ci#include <linux/errno.h> 5562306a36Sopenharmony_ci#include <linux/export.h> 5662306a36Sopenharmony_ci#include <linux/interrupt.h> 5762306a36Sopenharmony_ci#include <linux/module.h> 5862306a36Sopenharmony_ci#include <linux/pci.h> 5962306a36Sopenharmony_ci#include <linux/slab.h> 6062306a36Sopenharmony_ci#include <linux/types.h> 6162306a36Sopenharmony_ci#include <linux/uaccess.h> 6262306a36Sopenharmony_ci#include "linux/ntb.h" 6362306a36Sopenharmony_ci#include "linux/ntb_transport.h" 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define NTB_TRANSPORT_VERSION 4 6662306a36Sopenharmony_ci#define NTB_TRANSPORT_VER "4" 6762306a36Sopenharmony_ci#define NTB_TRANSPORT_NAME "ntb_transport" 6862306a36Sopenharmony_ci#define NTB_TRANSPORT_DESC "Software Queue-Pair Transport over NTB" 6962306a36Sopenharmony_ci#define NTB_TRANSPORT_MIN_SPADS (MW0_SZ_HIGH + 2) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciMODULE_DESCRIPTION(NTB_TRANSPORT_DESC); 7262306a36Sopenharmony_ciMODULE_VERSION(NTB_TRANSPORT_VER); 7362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 7462306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic unsigned long max_mw_size; 7762306a36Sopenharmony_cimodule_param(max_mw_size, ulong, 0644); 7862306a36Sopenharmony_ciMODULE_PARM_DESC(max_mw_size, "Limit size of large memory windows"); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic unsigned int transport_mtu = 0x10000; 8162306a36Sopenharmony_cimodule_param(transport_mtu, uint, 0644); 8262306a36Sopenharmony_ciMODULE_PARM_DESC(transport_mtu, "Maximum size of NTB transport packets"); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic unsigned char max_num_clients; 8562306a36Sopenharmony_cimodule_param(max_num_clients, byte, 0644); 8662306a36Sopenharmony_ciMODULE_PARM_DESC(max_num_clients, "Maximum number of NTB transport clients"); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned int copy_bytes = 1024; 8962306a36Sopenharmony_cimodule_param(copy_bytes, uint, 0644); 9062306a36Sopenharmony_ciMODULE_PARM_DESC(copy_bytes, "Threshold under which NTB will use the CPU to copy instead of DMA"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic bool use_dma; 9362306a36Sopenharmony_cimodule_param(use_dma, bool, 0644); 9462306a36Sopenharmony_ciMODULE_PARM_DESC(use_dma, "Use DMA engine to perform large data copy"); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic bool use_msi; 9762306a36Sopenharmony_ci#ifdef CONFIG_NTB_MSI 9862306a36Sopenharmony_cimodule_param(use_msi, bool, 0644); 9962306a36Sopenharmony_ciMODULE_PARM_DESC(use_msi, "Use MSI interrupts instead of doorbells"); 10062306a36Sopenharmony_ci#endif 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic struct dentry *nt_debugfs_dir; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Only two-ports NTB devices are supported */ 10562306a36Sopenharmony_ci#define PIDX NTB_DEF_PEER_IDX 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct ntb_queue_entry { 10862306a36Sopenharmony_ci /* ntb_queue list reference */ 10962306a36Sopenharmony_ci struct list_head entry; 11062306a36Sopenharmony_ci /* pointers to data to be transferred */ 11162306a36Sopenharmony_ci void *cb_data; 11262306a36Sopenharmony_ci void *buf; 11362306a36Sopenharmony_ci unsigned int len; 11462306a36Sopenharmony_ci unsigned int flags; 11562306a36Sopenharmony_ci int retries; 11662306a36Sopenharmony_ci int errors; 11762306a36Sopenharmony_ci unsigned int tx_index; 11862306a36Sopenharmony_ci unsigned int rx_index; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci struct ntb_transport_qp *qp; 12162306a36Sopenharmony_ci union { 12262306a36Sopenharmony_ci struct ntb_payload_header __iomem *tx_hdr; 12362306a36Sopenharmony_ci struct ntb_payload_header *rx_hdr; 12462306a36Sopenharmony_ci }; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistruct ntb_rx_info { 12862306a36Sopenharmony_ci unsigned int entry; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct ntb_transport_qp { 13262306a36Sopenharmony_ci struct ntb_transport_ctx *transport; 13362306a36Sopenharmony_ci struct ntb_dev *ndev; 13462306a36Sopenharmony_ci void *cb_data; 13562306a36Sopenharmony_ci struct dma_chan *tx_dma_chan; 13662306a36Sopenharmony_ci struct dma_chan *rx_dma_chan; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci bool client_ready; 13962306a36Sopenharmony_ci bool link_is_up; 14062306a36Sopenharmony_ci bool active; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci u8 qp_num; /* Only 64 QP's are allowed. 0-63 */ 14362306a36Sopenharmony_ci u64 qp_bit; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci struct ntb_rx_info __iomem *rx_info; 14662306a36Sopenharmony_ci struct ntb_rx_info *remote_rx_info; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci void (*tx_handler)(struct ntb_transport_qp *qp, void *qp_data, 14962306a36Sopenharmony_ci void *data, int len); 15062306a36Sopenharmony_ci struct list_head tx_free_q; 15162306a36Sopenharmony_ci spinlock_t ntb_tx_free_q_lock; 15262306a36Sopenharmony_ci void __iomem *tx_mw; 15362306a36Sopenharmony_ci phys_addr_t tx_mw_phys; 15462306a36Sopenharmony_ci size_t tx_mw_size; 15562306a36Sopenharmony_ci dma_addr_t tx_mw_dma_addr; 15662306a36Sopenharmony_ci unsigned int tx_index; 15762306a36Sopenharmony_ci unsigned int tx_max_entry; 15862306a36Sopenharmony_ci unsigned int tx_max_frame; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data, 16162306a36Sopenharmony_ci void *data, int len); 16262306a36Sopenharmony_ci struct list_head rx_post_q; 16362306a36Sopenharmony_ci struct list_head rx_pend_q; 16462306a36Sopenharmony_ci struct list_head rx_free_q; 16562306a36Sopenharmony_ci /* ntb_rx_q_lock: synchronize access to rx_XXXX_q */ 16662306a36Sopenharmony_ci spinlock_t ntb_rx_q_lock; 16762306a36Sopenharmony_ci void *rx_buff; 16862306a36Sopenharmony_ci unsigned int rx_index; 16962306a36Sopenharmony_ci unsigned int rx_max_entry; 17062306a36Sopenharmony_ci unsigned int rx_max_frame; 17162306a36Sopenharmony_ci unsigned int rx_alloc_entry; 17262306a36Sopenharmony_ci dma_cookie_t last_cookie; 17362306a36Sopenharmony_ci struct tasklet_struct rxc_db_work; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci void (*event_handler)(void *data, int status); 17662306a36Sopenharmony_ci struct delayed_work link_work; 17762306a36Sopenharmony_ci struct work_struct link_cleanup; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci struct dentry *debugfs_dir; 18062306a36Sopenharmony_ci struct dentry *debugfs_stats; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Stats */ 18362306a36Sopenharmony_ci u64 rx_bytes; 18462306a36Sopenharmony_ci u64 rx_pkts; 18562306a36Sopenharmony_ci u64 rx_ring_empty; 18662306a36Sopenharmony_ci u64 rx_err_no_buf; 18762306a36Sopenharmony_ci u64 rx_err_oflow; 18862306a36Sopenharmony_ci u64 rx_err_ver; 18962306a36Sopenharmony_ci u64 rx_memcpy; 19062306a36Sopenharmony_ci u64 rx_async; 19162306a36Sopenharmony_ci u64 tx_bytes; 19262306a36Sopenharmony_ci u64 tx_pkts; 19362306a36Sopenharmony_ci u64 tx_ring_full; 19462306a36Sopenharmony_ci u64 tx_err_no_buf; 19562306a36Sopenharmony_ci u64 tx_memcpy; 19662306a36Sopenharmony_ci u64 tx_async; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci bool use_msi; 19962306a36Sopenharmony_ci int msi_irq; 20062306a36Sopenharmony_ci struct ntb_msi_desc msi_desc; 20162306a36Sopenharmony_ci struct ntb_msi_desc peer_msi_desc; 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistruct ntb_transport_mw { 20562306a36Sopenharmony_ci phys_addr_t phys_addr; 20662306a36Sopenharmony_ci resource_size_t phys_size; 20762306a36Sopenharmony_ci void __iomem *vbase; 20862306a36Sopenharmony_ci size_t xlat_size; 20962306a36Sopenharmony_ci size_t buff_size; 21062306a36Sopenharmony_ci size_t alloc_size; 21162306a36Sopenharmony_ci void *alloc_addr; 21262306a36Sopenharmony_ci void *virt_addr; 21362306a36Sopenharmony_ci dma_addr_t dma_addr; 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct ntb_transport_client_dev { 21762306a36Sopenharmony_ci struct list_head entry; 21862306a36Sopenharmony_ci struct ntb_transport_ctx *nt; 21962306a36Sopenharmony_ci struct device dev; 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistruct ntb_transport_ctx { 22362306a36Sopenharmony_ci struct list_head entry; 22462306a36Sopenharmony_ci struct list_head client_devs; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci struct ntb_dev *ndev; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci struct ntb_transport_mw *mw_vec; 22962306a36Sopenharmony_ci struct ntb_transport_qp *qp_vec; 23062306a36Sopenharmony_ci unsigned int mw_count; 23162306a36Sopenharmony_ci unsigned int qp_count; 23262306a36Sopenharmony_ci u64 qp_bitmap; 23362306a36Sopenharmony_ci u64 qp_bitmap_free; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci bool use_msi; 23662306a36Sopenharmony_ci unsigned int msi_spad_offset; 23762306a36Sopenharmony_ci u64 msi_db_mask; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci bool link_is_up; 24062306a36Sopenharmony_ci struct delayed_work link_work; 24162306a36Sopenharmony_ci struct work_struct link_cleanup; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci struct dentry *debugfs_node_dir; 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cienum { 24762306a36Sopenharmony_ci DESC_DONE_FLAG = BIT(0), 24862306a36Sopenharmony_ci LINK_DOWN_FLAG = BIT(1), 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistruct ntb_payload_header { 25262306a36Sopenharmony_ci unsigned int ver; 25362306a36Sopenharmony_ci unsigned int len; 25462306a36Sopenharmony_ci unsigned int flags; 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cienum { 25862306a36Sopenharmony_ci VERSION = 0, 25962306a36Sopenharmony_ci QP_LINKS, 26062306a36Sopenharmony_ci NUM_QPS, 26162306a36Sopenharmony_ci NUM_MWS, 26262306a36Sopenharmony_ci MW0_SZ_HIGH, 26362306a36Sopenharmony_ci MW0_SZ_LOW, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#define dev_client_dev(__dev) \ 26762306a36Sopenharmony_ci container_of((__dev), struct ntb_transport_client_dev, dev) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define drv_client(__drv) \ 27062306a36Sopenharmony_ci container_of((__drv), struct ntb_transport_client, driver) 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci#define QP_TO_MW(nt, qp) ((qp) % nt->mw_count) 27362306a36Sopenharmony_ci#define NTB_QP_DEF_NUM_ENTRIES 100 27462306a36Sopenharmony_ci#define NTB_LINK_DOWN_TIMEOUT 10 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void ntb_transport_rxc_db(unsigned long data); 27762306a36Sopenharmony_cistatic const struct ntb_ctx_ops ntb_transport_ops; 27862306a36Sopenharmony_cistatic struct ntb_client ntb_transport_client; 27962306a36Sopenharmony_cistatic int ntb_async_tx_submit(struct ntb_transport_qp *qp, 28062306a36Sopenharmony_ci struct ntb_queue_entry *entry); 28162306a36Sopenharmony_cistatic void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset); 28262306a36Sopenharmony_cistatic int ntb_async_rx_submit(struct ntb_queue_entry *entry, void *offset); 28362306a36Sopenharmony_cistatic void ntb_memcpy_rx(struct ntb_queue_entry *entry, void *offset); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int ntb_transport_bus_match(struct device *dev, 28762306a36Sopenharmony_ci struct device_driver *drv) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return !strncmp(dev_name(dev), drv->name, strlen(drv->name)); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int ntb_transport_bus_probe(struct device *dev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci const struct ntb_transport_client *client; 29562306a36Sopenharmony_ci int rc; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci get_device(dev); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci client = drv_client(dev->driver); 30062306a36Sopenharmony_ci rc = client->probe(dev); 30162306a36Sopenharmony_ci if (rc) 30262306a36Sopenharmony_ci put_device(dev); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return rc; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void ntb_transport_bus_remove(struct device *dev) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci const struct ntb_transport_client *client; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci client = drv_client(dev->driver); 31262306a36Sopenharmony_ci client->remove(dev); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci put_device(dev); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic struct bus_type ntb_transport_bus = { 31862306a36Sopenharmony_ci .name = "ntb_transport", 31962306a36Sopenharmony_ci .match = ntb_transport_bus_match, 32062306a36Sopenharmony_ci .probe = ntb_transport_bus_probe, 32162306a36Sopenharmony_ci .remove = ntb_transport_bus_remove, 32262306a36Sopenharmony_ci}; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic LIST_HEAD(ntb_transport_list); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int ntb_bus_init(struct ntb_transport_ctx *nt) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci list_add_tail(&nt->entry, &ntb_transport_list); 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void ntb_bus_remove(struct ntb_transport_ctx *nt) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct ntb_transport_client_dev *client_dev, *cd; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci list_for_each_entry_safe(client_dev, cd, &nt->client_devs, entry) { 33762306a36Sopenharmony_ci dev_err(client_dev->dev.parent, "%s still attached to bus, removing\n", 33862306a36Sopenharmony_ci dev_name(&client_dev->dev)); 33962306a36Sopenharmony_ci list_del(&client_dev->entry); 34062306a36Sopenharmony_ci device_unregister(&client_dev->dev); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci list_del(&nt->entry); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void ntb_transport_client_release(struct device *dev) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct ntb_transport_client_dev *client_dev; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci client_dev = dev_client_dev(dev); 35162306a36Sopenharmony_ci kfree(client_dev); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * ntb_transport_unregister_client_dev - Unregister NTB client device 35662306a36Sopenharmony_ci * @device_name: Name of NTB client device 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * Unregister an NTB client device with the NTB transport layer 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_civoid ntb_transport_unregister_client_dev(char *device_name) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct ntb_transport_client_dev *client, *cd; 36362306a36Sopenharmony_ci struct ntb_transport_ctx *nt; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci list_for_each_entry(nt, &ntb_transport_list, entry) 36662306a36Sopenharmony_ci list_for_each_entry_safe(client, cd, &nt->client_devs, entry) 36762306a36Sopenharmony_ci if (!strncmp(dev_name(&client->dev), device_name, 36862306a36Sopenharmony_ci strlen(device_name))) { 36962306a36Sopenharmony_ci list_del(&client->entry); 37062306a36Sopenharmony_ci device_unregister(&client->dev); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_unregister_client_dev); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/** 37662306a36Sopenharmony_ci * ntb_transport_register_client_dev - Register NTB client device 37762306a36Sopenharmony_ci * @device_name: Name of NTB client device 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * Register an NTB client device with the NTB transport layer 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ciint ntb_transport_register_client_dev(char *device_name) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct ntb_transport_client_dev *client_dev; 38462306a36Sopenharmony_ci struct ntb_transport_ctx *nt; 38562306a36Sopenharmony_ci int node; 38662306a36Sopenharmony_ci int rc, i = 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (list_empty(&ntb_transport_list)) 38962306a36Sopenharmony_ci return -ENODEV; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci list_for_each_entry(nt, &ntb_transport_list, entry) { 39262306a36Sopenharmony_ci struct device *dev; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci node = dev_to_node(&nt->ndev->dev); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci client_dev = kzalloc_node(sizeof(*client_dev), 39762306a36Sopenharmony_ci GFP_KERNEL, node); 39862306a36Sopenharmony_ci if (!client_dev) { 39962306a36Sopenharmony_ci rc = -ENOMEM; 40062306a36Sopenharmony_ci goto err; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci dev = &client_dev->dev; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* setup and register client devices */ 40662306a36Sopenharmony_ci dev_set_name(dev, "%s%d", device_name, i); 40762306a36Sopenharmony_ci dev->bus = &ntb_transport_bus; 40862306a36Sopenharmony_ci dev->release = ntb_transport_client_release; 40962306a36Sopenharmony_ci dev->parent = &nt->ndev->dev; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci rc = device_register(dev); 41262306a36Sopenharmony_ci if (rc) { 41362306a36Sopenharmony_ci put_device(dev); 41462306a36Sopenharmony_ci goto err; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci list_add_tail(&client_dev->entry, &nt->client_devs); 41862306a36Sopenharmony_ci i++; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cierr: 42462306a36Sopenharmony_ci ntb_transport_unregister_client_dev(device_name); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return rc; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_register_client_dev); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/** 43162306a36Sopenharmony_ci * ntb_transport_register_client - Register NTB client driver 43262306a36Sopenharmony_ci * @drv: NTB client driver to be registered 43362306a36Sopenharmony_ci * 43462306a36Sopenharmony_ci * Register an NTB client driver with the NTB transport layer 43562306a36Sopenharmony_ci * 43662306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ciint ntb_transport_register_client(struct ntb_transport_client *drv) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci drv->driver.bus = &ntb_transport_bus; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (list_empty(&ntb_transport_list)) 44362306a36Sopenharmony_ci return -ENODEV; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return driver_register(&drv->driver); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_register_client); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/** 45062306a36Sopenharmony_ci * ntb_transport_unregister_client - Unregister NTB client driver 45162306a36Sopenharmony_ci * @drv: NTB client driver to be unregistered 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * Unregister an NTB client driver with the NTB transport layer 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_civoid ntb_transport_unregister_client(struct ntb_transport_client *drv) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci driver_unregister(&drv->driver); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_unregister_client); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, 46462306a36Sopenharmony_ci loff_t *offp) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct ntb_transport_qp *qp; 46762306a36Sopenharmony_ci char *buf; 46862306a36Sopenharmony_ci ssize_t ret, out_offset, out_count; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci qp = filp->private_data; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!qp || !qp->link_is_up) 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci out_count = 1000; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci buf = kmalloc(out_count, GFP_KERNEL); 47862306a36Sopenharmony_ci if (!buf) 47962306a36Sopenharmony_ci return -ENOMEM; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci out_offset = 0; 48262306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 48362306a36Sopenharmony_ci "\nNTB QP stats:\n\n"); 48462306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 48562306a36Sopenharmony_ci "rx_bytes - \t%llu\n", qp->rx_bytes); 48662306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 48762306a36Sopenharmony_ci "rx_pkts - \t%llu\n", qp->rx_pkts); 48862306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 48962306a36Sopenharmony_ci "rx_memcpy - \t%llu\n", qp->rx_memcpy); 49062306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 49162306a36Sopenharmony_ci "rx_async - \t%llu\n", qp->rx_async); 49262306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 49362306a36Sopenharmony_ci "rx_ring_empty - %llu\n", qp->rx_ring_empty); 49462306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 49562306a36Sopenharmony_ci "rx_err_no_buf - %llu\n", qp->rx_err_no_buf); 49662306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 49762306a36Sopenharmony_ci "rx_err_oflow - \t%llu\n", qp->rx_err_oflow); 49862306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 49962306a36Sopenharmony_ci "rx_err_ver - \t%llu\n", qp->rx_err_ver); 50062306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 50162306a36Sopenharmony_ci "rx_buff - \t0x%p\n", qp->rx_buff); 50262306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 50362306a36Sopenharmony_ci "rx_index - \t%u\n", qp->rx_index); 50462306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 50562306a36Sopenharmony_ci "rx_max_entry - \t%u\n", qp->rx_max_entry); 50662306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 50762306a36Sopenharmony_ci "rx_alloc_entry - \t%u\n\n", qp->rx_alloc_entry); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 51062306a36Sopenharmony_ci "tx_bytes - \t%llu\n", qp->tx_bytes); 51162306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 51262306a36Sopenharmony_ci "tx_pkts - \t%llu\n", qp->tx_pkts); 51362306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 51462306a36Sopenharmony_ci "tx_memcpy - \t%llu\n", qp->tx_memcpy); 51562306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 51662306a36Sopenharmony_ci "tx_async - \t%llu\n", qp->tx_async); 51762306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 51862306a36Sopenharmony_ci "tx_ring_full - \t%llu\n", qp->tx_ring_full); 51962306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 52062306a36Sopenharmony_ci "tx_err_no_buf - %llu\n", qp->tx_err_no_buf); 52162306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 52262306a36Sopenharmony_ci "tx_mw - \t0x%p\n", qp->tx_mw); 52362306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 52462306a36Sopenharmony_ci "tx_index (H) - \t%u\n", qp->tx_index); 52562306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 52662306a36Sopenharmony_ci "RRI (T) - \t%u\n", 52762306a36Sopenharmony_ci qp->remote_rx_info->entry); 52862306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 52962306a36Sopenharmony_ci "tx_max_entry - \t%u\n", qp->tx_max_entry); 53062306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 53162306a36Sopenharmony_ci "free tx - \t%u\n", 53262306a36Sopenharmony_ci ntb_transport_tx_free_entry(qp)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 53562306a36Sopenharmony_ci "\n"); 53662306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 53762306a36Sopenharmony_ci "Using TX DMA - \t%s\n", 53862306a36Sopenharmony_ci qp->tx_dma_chan ? "Yes" : "No"); 53962306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 54062306a36Sopenharmony_ci "Using RX DMA - \t%s\n", 54162306a36Sopenharmony_ci qp->rx_dma_chan ? "Yes" : "No"); 54262306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 54362306a36Sopenharmony_ci "QP Link - \t%s\n", 54462306a36Sopenharmony_ci qp->link_is_up ? "Up" : "Down"); 54562306a36Sopenharmony_ci out_offset += scnprintf(buf + out_offset, out_count - out_offset, 54662306a36Sopenharmony_ci "\n"); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (out_offset > out_count) 54962306a36Sopenharmony_ci out_offset = out_count; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); 55262306a36Sopenharmony_ci kfree(buf); 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic const struct file_operations ntb_qp_debugfs_stats = { 55762306a36Sopenharmony_ci .owner = THIS_MODULE, 55862306a36Sopenharmony_ci .open = simple_open, 55962306a36Sopenharmony_ci .read = debugfs_read, 56062306a36Sopenharmony_ci}; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void ntb_list_add(spinlock_t *lock, struct list_head *entry, 56362306a36Sopenharmony_ci struct list_head *list) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci unsigned long flags; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci spin_lock_irqsave(lock, flags); 56862306a36Sopenharmony_ci list_add_tail(entry, list); 56962306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic struct ntb_queue_entry *ntb_list_rm(spinlock_t *lock, 57362306a36Sopenharmony_ci struct list_head *list) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct ntb_queue_entry *entry; 57662306a36Sopenharmony_ci unsigned long flags; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci spin_lock_irqsave(lock, flags); 57962306a36Sopenharmony_ci if (list_empty(list)) { 58062306a36Sopenharmony_ci entry = NULL; 58162306a36Sopenharmony_ci goto out; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci entry = list_first_entry(list, struct ntb_queue_entry, entry); 58462306a36Sopenharmony_ci list_del(&entry->entry); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciout: 58762306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return entry; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic struct ntb_queue_entry *ntb_list_mv(spinlock_t *lock, 59362306a36Sopenharmony_ci struct list_head *list, 59462306a36Sopenharmony_ci struct list_head *to_list) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct ntb_queue_entry *entry; 59762306a36Sopenharmony_ci unsigned long flags; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci spin_lock_irqsave(lock, flags); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (list_empty(list)) { 60262306a36Sopenharmony_ci entry = NULL; 60362306a36Sopenharmony_ci } else { 60462306a36Sopenharmony_ci entry = list_first_entry(list, struct ntb_queue_entry, entry); 60562306a36Sopenharmony_ci list_move_tail(&entry->entry, to_list); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return entry; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, 61462306a36Sopenharmony_ci unsigned int qp_num) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct ntb_transport_qp *qp = &nt->qp_vec[qp_num]; 61762306a36Sopenharmony_ci struct ntb_transport_mw *mw; 61862306a36Sopenharmony_ci struct ntb_dev *ndev = nt->ndev; 61962306a36Sopenharmony_ci struct ntb_queue_entry *entry; 62062306a36Sopenharmony_ci unsigned int rx_size, num_qps_mw; 62162306a36Sopenharmony_ci unsigned int mw_num, mw_count, qp_count; 62262306a36Sopenharmony_ci unsigned int i; 62362306a36Sopenharmony_ci int node; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci mw_count = nt->mw_count; 62662306a36Sopenharmony_ci qp_count = nt->qp_count; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mw_num = QP_TO_MW(nt, qp_num); 62962306a36Sopenharmony_ci mw = &nt->mw_vec[mw_num]; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (!mw->virt_addr) 63262306a36Sopenharmony_ci return -ENOMEM; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (mw_num < qp_count % mw_count) 63562306a36Sopenharmony_ci num_qps_mw = qp_count / mw_count + 1; 63662306a36Sopenharmony_ci else 63762306a36Sopenharmony_ci num_qps_mw = qp_count / mw_count; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci rx_size = (unsigned int)mw->xlat_size / num_qps_mw; 64062306a36Sopenharmony_ci qp->rx_buff = mw->virt_addr + rx_size * (qp_num / mw_count); 64162306a36Sopenharmony_ci rx_size -= sizeof(struct ntb_rx_info); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci qp->remote_rx_info = qp->rx_buff + rx_size; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Due to housekeeping, there must be atleast 2 buffs */ 64662306a36Sopenharmony_ci qp->rx_max_frame = min(transport_mtu, rx_size / 2); 64762306a36Sopenharmony_ci qp->rx_max_entry = rx_size / qp->rx_max_frame; 64862306a36Sopenharmony_ci qp->rx_index = 0; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * Checking to see if we have more entries than the default. 65262306a36Sopenharmony_ci * We should add additional entries if that is the case so we 65362306a36Sopenharmony_ci * can be in sync with the transport frames. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_ci node = dev_to_node(&ndev->dev); 65662306a36Sopenharmony_ci for (i = qp->rx_alloc_entry; i < qp->rx_max_entry; i++) { 65762306a36Sopenharmony_ci entry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node); 65862306a36Sopenharmony_ci if (!entry) 65962306a36Sopenharmony_ci return -ENOMEM; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci entry->qp = qp; 66262306a36Sopenharmony_ci ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, 66362306a36Sopenharmony_ci &qp->rx_free_q); 66462306a36Sopenharmony_ci qp->rx_alloc_entry++; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci qp->remote_rx_info->entry = qp->rx_max_entry - 1; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* setup the hdr offsets with 0's */ 67062306a36Sopenharmony_ci for (i = 0; i < qp->rx_max_entry; i++) { 67162306a36Sopenharmony_ci void *offset = (qp->rx_buff + qp->rx_max_frame * (i + 1) - 67262306a36Sopenharmony_ci sizeof(struct ntb_payload_header)); 67362306a36Sopenharmony_ci memset(offset, 0, sizeof(struct ntb_payload_header)); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci qp->rx_pkts = 0; 67762306a36Sopenharmony_ci qp->tx_pkts = 0; 67862306a36Sopenharmony_ci qp->tx_index = 0; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic irqreturn_t ntb_transport_isr(int irq, void *dev) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct ntb_transport_qp *qp = dev; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci tasklet_schedule(&qp->rxc_db_work); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return IRQ_HANDLED; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void ntb_transport_setup_qp_peer_msi(struct ntb_transport_ctx *nt, 69362306a36Sopenharmony_ci unsigned int qp_num) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct ntb_transport_qp *qp = &nt->qp_vec[qp_num]; 69662306a36Sopenharmony_ci int spad = qp_num * 2 + nt->msi_spad_offset; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (!nt->use_msi) 69962306a36Sopenharmony_ci return; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (spad >= ntb_spad_count(nt->ndev)) 70262306a36Sopenharmony_ci return; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci qp->peer_msi_desc.addr_offset = 70562306a36Sopenharmony_ci ntb_peer_spad_read(qp->ndev, PIDX, spad); 70662306a36Sopenharmony_ci qp->peer_msi_desc.data = 70762306a36Sopenharmony_ci ntb_peer_spad_read(qp->ndev, PIDX, spad + 1); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "QP%d Peer MSI addr=%x data=%x\n", 71062306a36Sopenharmony_ci qp_num, qp->peer_msi_desc.addr_offset, qp->peer_msi_desc.data); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (qp->peer_msi_desc.addr_offset) { 71362306a36Sopenharmony_ci qp->use_msi = true; 71462306a36Sopenharmony_ci dev_info(&qp->ndev->pdev->dev, 71562306a36Sopenharmony_ci "Using MSI interrupts for QP%d\n", qp_num); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void ntb_transport_setup_qp_msi(struct ntb_transport_ctx *nt, 72062306a36Sopenharmony_ci unsigned int qp_num) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct ntb_transport_qp *qp = &nt->qp_vec[qp_num]; 72362306a36Sopenharmony_ci int spad = qp_num * 2 + nt->msi_spad_offset; 72462306a36Sopenharmony_ci int rc; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (!nt->use_msi) 72762306a36Sopenharmony_ci return; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (spad >= ntb_spad_count(nt->ndev)) { 73062306a36Sopenharmony_ci dev_warn_once(&qp->ndev->pdev->dev, 73162306a36Sopenharmony_ci "Not enough SPADS to use MSI interrupts\n"); 73262306a36Sopenharmony_ci return; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ntb_spad_write(qp->ndev, spad, 0); 73662306a36Sopenharmony_ci ntb_spad_write(qp->ndev, spad + 1, 0); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!qp->msi_irq) { 73962306a36Sopenharmony_ci qp->msi_irq = ntbm_msi_request_irq(qp->ndev, ntb_transport_isr, 74062306a36Sopenharmony_ci KBUILD_MODNAME, qp, 74162306a36Sopenharmony_ci &qp->msi_desc); 74262306a36Sopenharmony_ci if (qp->msi_irq < 0) { 74362306a36Sopenharmony_ci dev_warn(&qp->ndev->pdev->dev, 74462306a36Sopenharmony_ci "Unable to allocate MSI interrupt for qp%d\n", 74562306a36Sopenharmony_ci qp_num); 74662306a36Sopenharmony_ci return; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci rc = ntb_spad_write(qp->ndev, spad, qp->msi_desc.addr_offset); 75162306a36Sopenharmony_ci if (rc) 75262306a36Sopenharmony_ci goto err_free_interrupt; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci rc = ntb_spad_write(qp->ndev, spad + 1, qp->msi_desc.data); 75562306a36Sopenharmony_ci if (rc) 75662306a36Sopenharmony_ci goto err_free_interrupt; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "QP%d MSI %d addr=%x data=%x\n", 75962306a36Sopenharmony_ci qp_num, qp->msi_irq, qp->msi_desc.addr_offset, 76062306a36Sopenharmony_ci qp->msi_desc.data); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cierr_free_interrupt: 76562306a36Sopenharmony_ci devm_free_irq(&nt->ndev->dev, qp->msi_irq, qp); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void ntb_transport_msi_peer_desc_changed(struct ntb_transport_ctx *nt) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci int i; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci dev_dbg(&nt->ndev->pdev->dev, "Peer MSI descriptors changed"); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci for (i = 0; i < nt->qp_count; i++) 77562306a36Sopenharmony_ci ntb_transport_setup_qp_peer_msi(nt, i); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic void ntb_transport_msi_desc_changed(void *data) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct ntb_transport_ctx *nt = data; 78162306a36Sopenharmony_ci int i; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci dev_dbg(&nt->ndev->pdev->dev, "MSI descriptors changed"); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci for (i = 0; i < nt->qp_count; i++) 78662306a36Sopenharmony_ci ntb_transport_setup_qp_msi(nt, i); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ntb_peer_db_set(nt->ndev, nt->msi_db_mask); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; 79462306a36Sopenharmony_ci struct pci_dev *pdev = nt->ndev->pdev; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (!mw->virt_addr) 79762306a36Sopenharmony_ci return; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ntb_mw_clear_trans(nt->ndev, PIDX, num_mw); 80062306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, mw->alloc_size, 80162306a36Sopenharmony_ci mw->alloc_addr, mw->dma_addr); 80262306a36Sopenharmony_ci mw->xlat_size = 0; 80362306a36Sopenharmony_ci mw->buff_size = 0; 80462306a36Sopenharmony_ci mw->alloc_size = 0; 80562306a36Sopenharmony_ci mw->alloc_addr = NULL; 80662306a36Sopenharmony_ci mw->virt_addr = NULL; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic int ntb_alloc_mw_buffer(struct ntb_transport_mw *mw, 81062306a36Sopenharmony_ci struct device *dma_dev, size_t align) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci dma_addr_t dma_addr; 81362306a36Sopenharmony_ci void *alloc_addr, *virt_addr; 81462306a36Sopenharmony_ci int rc; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci alloc_addr = dma_alloc_coherent(dma_dev, mw->alloc_size, 81762306a36Sopenharmony_ci &dma_addr, GFP_KERNEL); 81862306a36Sopenharmony_ci if (!alloc_addr) { 81962306a36Sopenharmony_ci dev_err(dma_dev, "Unable to alloc MW buff of size %zu\n", 82062306a36Sopenharmony_ci mw->alloc_size); 82162306a36Sopenharmony_ci return -ENOMEM; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci virt_addr = alloc_addr; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * we must ensure that the memory address allocated is BAR size 82762306a36Sopenharmony_ci * aligned in order for the XLAT register to take the value. This 82862306a36Sopenharmony_ci * is a requirement of the hardware. It is recommended to setup CMA 82962306a36Sopenharmony_ci * for BAR sizes equal or greater than 4MB. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci if (!IS_ALIGNED(dma_addr, align)) { 83262306a36Sopenharmony_ci if (mw->alloc_size > mw->buff_size) { 83362306a36Sopenharmony_ci virt_addr = PTR_ALIGN(alloc_addr, align); 83462306a36Sopenharmony_ci dma_addr = ALIGN(dma_addr, align); 83562306a36Sopenharmony_ci } else { 83662306a36Sopenharmony_ci rc = -ENOMEM; 83762306a36Sopenharmony_ci goto err; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci mw->alloc_addr = alloc_addr; 84262306a36Sopenharmony_ci mw->virt_addr = virt_addr; 84362306a36Sopenharmony_ci mw->dma_addr = dma_addr; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cierr: 84862306a36Sopenharmony_ci dma_free_coherent(dma_dev, mw->alloc_size, alloc_addr, dma_addr); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return rc; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, 85462306a36Sopenharmony_ci resource_size_t size) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; 85762306a36Sopenharmony_ci struct pci_dev *pdev = nt->ndev->pdev; 85862306a36Sopenharmony_ci size_t xlat_size, buff_size; 85962306a36Sopenharmony_ci resource_size_t xlat_align; 86062306a36Sopenharmony_ci resource_size_t xlat_align_size; 86162306a36Sopenharmony_ci int rc; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (!size) 86462306a36Sopenharmony_ci return -EINVAL; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci rc = ntb_mw_get_align(nt->ndev, PIDX, num_mw, &xlat_align, 86762306a36Sopenharmony_ci &xlat_align_size, NULL); 86862306a36Sopenharmony_ci if (rc) 86962306a36Sopenharmony_ci return rc; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci xlat_size = round_up(size, xlat_align_size); 87262306a36Sopenharmony_ci buff_size = round_up(size, xlat_align); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* No need to re-setup */ 87562306a36Sopenharmony_ci if (mw->xlat_size == xlat_size) 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (mw->buff_size) 87962306a36Sopenharmony_ci ntb_free_mw(nt, num_mw); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Alloc memory for receiving data. Must be aligned */ 88262306a36Sopenharmony_ci mw->xlat_size = xlat_size; 88362306a36Sopenharmony_ci mw->buff_size = buff_size; 88462306a36Sopenharmony_ci mw->alloc_size = buff_size; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align); 88762306a36Sopenharmony_ci if (rc) { 88862306a36Sopenharmony_ci mw->alloc_size *= 2; 88962306a36Sopenharmony_ci rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align); 89062306a36Sopenharmony_ci if (rc) { 89162306a36Sopenharmony_ci dev_err(&pdev->dev, 89262306a36Sopenharmony_ci "Unable to alloc aligned MW buff\n"); 89362306a36Sopenharmony_ci mw->xlat_size = 0; 89462306a36Sopenharmony_ci mw->buff_size = 0; 89562306a36Sopenharmony_ci mw->alloc_size = 0; 89662306a36Sopenharmony_ci return rc; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* Notify HW the memory location of the receive buffer */ 90162306a36Sopenharmony_ci rc = ntb_mw_set_trans(nt->ndev, PIDX, num_mw, mw->dma_addr, 90262306a36Sopenharmony_ci mw->xlat_size); 90362306a36Sopenharmony_ci if (rc) { 90462306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to set mw%d translation", num_mw); 90562306a36Sopenharmony_ci ntb_free_mw(nt, num_mw); 90662306a36Sopenharmony_ci return -EIO; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return 0; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic void ntb_qp_link_context_reset(struct ntb_transport_qp *qp) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci qp->link_is_up = false; 91562306a36Sopenharmony_ci qp->active = false; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci qp->tx_index = 0; 91862306a36Sopenharmony_ci qp->rx_index = 0; 91962306a36Sopenharmony_ci qp->rx_bytes = 0; 92062306a36Sopenharmony_ci qp->rx_pkts = 0; 92162306a36Sopenharmony_ci qp->rx_ring_empty = 0; 92262306a36Sopenharmony_ci qp->rx_err_no_buf = 0; 92362306a36Sopenharmony_ci qp->rx_err_oflow = 0; 92462306a36Sopenharmony_ci qp->rx_err_ver = 0; 92562306a36Sopenharmony_ci qp->rx_memcpy = 0; 92662306a36Sopenharmony_ci qp->rx_async = 0; 92762306a36Sopenharmony_ci qp->tx_bytes = 0; 92862306a36Sopenharmony_ci qp->tx_pkts = 0; 92962306a36Sopenharmony_ci qp->tx_ring_full = 0; 93062306a36Sopenharmony_ci qp->tx_err_no_buf = 0; 93162306a36Sopenharmony_ci qp->tx_memcpy = 0; 93262306a36Sopenharmony_ci qp->tx_async = 0; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic void ntb_qp_link_down_reset(struct ntb_transport_qp *qp) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci ntb_qp_link_context_reset(qp); 93862306a36Sopenharmony_ci if (qp->remote_rx_info) 93962306a36Sopenharmony_ci qp->remote_rx_info->entry = qp->rx_max_entry - 1; 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic void ntb_qp_link_cleanup(struct ntb_transport_qp *qp) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct ntb_transport_ctx *nt = qp->transport; 94562306a36Sopenharmony_ci struct pci_dev *pdev = nt->ndev->pdev; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci dev_info(&pdev->dev, "qp %d: Link Cleanup\n", qp->qp_num); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci cancel_delayed_work_sync(&qp->link_work); 95062306a36Sopenharmony_ci ntb_qp_link_down_reset(qp); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (qp->event_handler) 95362306a36Sopenharmony_ci qp->event_handler(qp->cb_data, qp->link_is_up); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic void ntb_qp_link_cleanup_work(struct work_struct *work) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci struct ntb_transport_qp *qp = container_of(work, 95962306a36Sopenharmony_ci struct ntb_transport_qp, 96062306a36Sopenharmony_ci link_cleanup); 96162306a36Sopenharmony_ci struct ntb_transport_ctx *nt = qp->transport; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci ntb_qp_link_cleanup(qp); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (nt->link_is_up) 96662306a36Sopenharmony_ci schedule_delayed_work(&qp->link_work, 96762306a36Sopenharmony_ci msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT)); 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic void ntb_qp_link_down(struct ntb_transport_qp *qp) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci schedule_work(&qp->link_cleanup); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct ntb_transport_qp *qp; 97862306a36Sopenharmony_ci u64 qp_bitmap_alloc; 97962306a36Sopenharmony_ci unsigned int i, count; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci qp_bitmap_alloc = nt->qp_bitmap & ~nt->qp_bitmap_free; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Pass along the info to any clients */ 98462306a36Sopenharmony_ci for (i = 0; i < nt->qp_count; i++) 98562306a36Sopenharmony_ci if (qp_bitmap_alloc & BIT_ULL(i)) { 98662306a36Sopenharmony_ci qp = &nt->qp_vec[i]; 98762306a36Sopenharmony_ci ntb_qp_link_cleanup(qp); 98862306a36Sopenharmony_ci cancel_work_sync(&qp->link_cleanup); 98962306a36Sopenharmony_ci cancel_delayed_work_sync(&qp->link_work); 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (!nt->link_is_up) 99362306a36Sopenharmony_ci cancel_delayed_work_sync(&nt->link_work); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci for (i = 0; i < nt->mw_count; i++) 99662306a36Sopenharmony_ci ntb_free_mw(nt, i); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* The scratchpad registers keep the values if the remote side 99962306a36Sopenharmony_ci * goes down, blast them now to give them a sane value the next 100062306a36Sopenharmony_ci * time they are accessed 100162306a36Sopenharmony_ci */ 100262306a36Sopenharmony_ci count = ntb_spad_count(nt->ndev); 100362306a36Sopenharmony_ci for (i = 0; i < count; i++) 100462306a36Sopenharmony_ci ntb_spad_write(nt->ndev, i, 0); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic void ntb_transport_link_cleanup_work(struct work_struct *work) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct ntb_transport_ctx *nt = 101062306a36Sopenharmony_ci container_of(work, struct ntb_transport_ctx, link_cleanup); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci ntb_transport_link_cleanup(nt); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic void ntb_transport_event_callback(void *data) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct ntb_transport_ctx *nt = data; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (ntb_link_is_up(nt->ndev, NULL, NULL) == 1) 102062306a36Sopenharmony_ci schedule_delayed_work(&nt->link_work, 0); 102162306a36Sopenharmony_ci else 102262306a36Sopenharmony_ci schedule_work(&nt->link_cleanup); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic void ntb_transport_link_work(struct work_struct *work) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct ntb_transport_ctx *nt = 102862306a36Sopenharmony_ci container_of(work, struct ntb_transport_ctx, link_work.work); 102962306a36Sopenharmony_ci struct ntb_dev *ndev = nt->ndev; 103062306a36Sopenharmony_ci struct pci_dev *pdev = ndev->pdev; 103162306a36Sopenharmony_ci resource_size_t size; 103262306a36Sopenharmony_ci u32 val; 103362306a36Sopenharmony_ci int rc = 0, i, spad; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* send the local info, in the opposite order of the way we read it */ 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (nt->use_msi) { 103862306a36Sopenharmony_ci rc = ntb_msi_setup_mws(ndev); 103962306a36Sopenharmony_ci if (rc) { 104062306a36Sopenharmony_ci dev_warn(&pdev->dev, 104162306a36Sopenharmony_ci "Failed to register MSI memory window: %d\n", 104262306a36Sopenharmony_ci rc); 104362306a36Sopenharmony_ci nt->use_msi = false; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci for (i = 0; i < nt->qp_count; i++) 104862306a36Sopenharmony_ci ntb_transport_setup_qp_msi(nt, i); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci for (i = 0; i < nt->mw_count; i++) { 105162306a36Sopenharmony_ci size = nt->mw_vec[i].phys_size; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (max_mw_size && size > max_mw_size) 105462306a36Sopenharmony_ci size = max_mw_size; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci spad = MW0_SZ_HIGH + (i * 2); 105762306a36Sopenharmony_ci ntb_peer_spad_write(ndev, PIDX, spad, upper_32_bits(size)); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci spad = MW0_SZ_LOW + (i * 2); 106062306a36Sopenharmony_ci ntb_peer_spad_write(ndev, PIDX, spad, lower_32_bits(size)); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci ntb_peer_spad_write(ndev, PIDX, NUM_MWS, nt->mw_count); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci ntb_peer_spad_write(ndev, PIDX, NUM_QPS, nt->qp_count); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci ntb_peer_spad_write(ndev, PIDX, VERSION, NTB_TRANSPORT_VERSION); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Query the remote side for its info */ 107062306a36Sopenharmony_ci val = ntb_spad_read(ndev, VERSION); 107162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Remote version = %d\n", val); 107262306a36Sopenharmony_ci if (val != NTB_TRANSPORT_VERSION) 107362306a36Sopenharmony_ci goto out; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci val = ntb_spad_read(ndev, NUM_QPS); 107662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Remote max number of qps = %d\n", val); 107762306a36Sopenharmony_ci if (val != nt->qp_count) 107862306a36Sopenharmony_ci goto out; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci val = ntb_spad_read(ndev, NUM_MWS); 108162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Remote number of mws = %d\n", val); 108262306a36Sopenharmony_ci if (val != nt->mw_count) 108362306a36Sopenharmony_ci goto out; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci for (i = 0; i < nt->mw_count; i++) { 108662306a36Sopenharmony_ci u64 val64; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci val = ntb_spad_read(ndev, MW0_SZ_HIGH + (i * 2)); 108962306a36Sopenharmony_ci val64 = (u64)val << 32; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci val = ntb_spad_read(ndev, MW0_SZ_LOW + (i * 2)); 109262306a36Sopenharmony_ci val64 |= val; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Remote MW%d size = %#llx\n", i, val64); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci rc = ntb_set_mw(nt, i, val64); 109762306a36Sopenharmony_ci if (rc) 109862306a36Sopenharmony_ci goto out1; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci nt->link_is_up = true; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci for (i = 0; i < nt->qp_count; i++) { 110462306a36Sopenharmony_ci struct ntb_transport_qp *qp = &nt->qp_vec[i]; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci ntb_transport_setup_qp_mw(nt, i); 110762306a36Sopenharmony_ci ntb_transport_setup_qp_peer_msi(nt, i); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (qp->client_ready) 111062306a36Sopenharmony_ci schedule_delayed_work(&qp->link_work, 0); 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci return; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ciout1: 111662306a36Sopenharmony_ci for (i = 0; i < nt->mw_count; i++) 111762306a36Sopenharmony_ci ntb_free_mw(nt, i); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* if there's an actual failure, we should just bail */ 112062306a36Sopenharmony_ci if (rc < 0) 112162306a36Sopenharmony_ci return; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ciout: 112462306a36Sopenharmony_ci if (ntb_link_is_up(ndev, NULL, NULL) == 1) 112562306a36Sopenharmony_ci schedule_delayed_work(&nt->link_work, 112662306a36Sopenharmony_ci msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT)); 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic void ntb_qp_link_work(struct work_struct *work) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct ntb_transport_qp *qp = container_of(work, 113262306a36Sopenharmony_ci struct ntb_transport_qp, 113362306a36Sopenharmony_ci link_work.work); 113462306a36Sopenharmony_ci struct pci_dev *pdev = qp->ndev->pdev; 113562306a36Sopenharmony_ci struct ntb_transport_ctx *nt = qp->transport; 113662306a36Sopenharmony_ci int val; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci WARN_ON(!nt->link_is_up); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci val = ntb_spad_read(nt->ndev, QP_LINKS); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci ntb_peer_spad_write(nt->ndev, PIDX, QP_LINKS, val | BIT(qp->qp_num)); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* query remote spad for qp ready bits */ 114562306a36Sopenharmony_ci dev_dbg_ratelimited(&pdev->dev, "Remote QP link status = %x\n", val); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* See if the remote side is up */ 114862306a36Sopenharmony_ci if (val & BIT(qp->qp_num)) { 114962306a36Sopenharmony_ci dev_info(&pdev->dev, "qp %d: Link Up\n", qp->qp_num); 115062306a36Sopenharmony_ci qp->link_is_up = true; 115162306a36Sopenharmony_ci qp->active = true; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (qp->event_handler) 115462306a36Sopenharmony_ci qp->event_handler(qp->cb_data, qp->link_is_up); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (qp->active) 115762306a36Sopenharmony_ci tasklet_schedule(&qp->rxc_db_work); 115862306a36Sopenharmony_ci } else if (nt->link_is_up) 115962306a36Sopenharmony_ci schedule_delayed_work(&qp->link_work, 116062306a36Sopenharmony_ci msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT)); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic int ntb_transport_init_queue(struct ntb_transport_ctx *nt, 116462306a36Sopenharmony_ci unsigned int qp_num) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct ntb_transport_qp *qp; 116762306a36Sopenharmony_ci phys_addr_t mw_base; 116862306a36Sopenharmony_ci resource_size_t mw_size; 116962306a36Sopenharmony_ci unsigned int num_qps_mw, tx_size; 117062306a36Sopenharmony_ci unsigned int mw_num, mw_count, qp_count; 117162306a36Sopenharmony_ci u64 qp_offset; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci mw_count = nt->mw_count; 117462306a36Sopenharmony_ci qp_count = nt->qp_count; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci mw_num = QP_TO_MW(nt, qp_num); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci qp = &nt->qp_vec[qp_num]; 117962306a36Sopenharmony_ci qp->qp_num = qp_num; 118062306a36Sopenharmony_ci qp->transport = nt; 118162306a36Sopenharmony_ci qp->ndev = nt->ndev; 118262306a36Sopenharmony_ci qp->client_ready = false; 118362306a36Sopenharmony_ci qp->event_handler = NULL; 118462306a36Sopenharmony_ci ntb_qp_link_context_reset(qp); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (mw_num < qp_count % mw_count) 118762306a36Sopenharmony_ci num_qps_mw = qp_count / mw_count + 1; 118862306a36Sopenharmony_ci else 118962306a36Sopenharmony_ci num_qps_mw = qp_count / mw_count; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci mw_base = nt->mw_vec[mw_num].phys_addr; 119262306a36Sopenharmony_ci mw_size = nt->mw_vec[mw_num].phys_size; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (max_mw_size && mw_size > max_mw_size) 119562306a36Sopenharmony_ci mw_size = max_mw_size; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci tx_size = (unsigned int)mw_size / num_qps_mw; 119862306a36Sopenharmony_ci qp_offset = tx_size * (qp_num / mw_count); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci qp->tx_mw_size = tx_size; 120162306a36Sopenharmony_ci qp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset; 120262306a36Sopenharmony_ci if (!qp->tx_mw) 120362306a36Sopenharmony_ci return -EINVAL; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci qp->tx_mw_phys = mw_base + qp_offset; 120662306a36Sopenharmony_ci if (!qp->tx_mw_phys) 120762306a36Sopenharmony_ci return -EINVAL; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci tx_size -= sizeof(struct ntb_rx_info); 121062306a36Sopenharmony_ci qp->rx_info = qp->tx_mw + tx_size; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* Due to housekeeping, there must be atleast 2 buffs */ 121362306a36Sopenharmony_ci qp->tx_max_frame = min(transport_mtu, tx_size / 2); 121462306a36Sopenharmony_ci qp->tx_max_entry = tx_size / qp->tx_max_frame; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (nt->debugfs_node_dir) { 121762306a36Sopenharmony_ci char debugfs_name[4]; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci snprintf(debugfs_name, 4, "qp%d", qp_num); 122062306a36Sopenharmony_ci qp->debugfs_dir = debugfs_create_dir(debugfs_name, 122162306a36Sopenharmony_ci nt->debugfs_node_dir); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci qp->debugfs_stats = debugfs_create_file("stats", S_IRUSR, 122462306a36Sopenharmony_ci qp->debugfs_dir, qp, 122562306a36Sopenharmony_ci &ntb_qp_debugfs_stats); 122662306a36Sopenharmony_ci } else { 122762306a36Sopenharmony_ci qp->debugfs_dir = NULL; 122862306a36Sopenharmony_ci qp->debugfs_stats = NULL; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci INIT_DELAYED_WORK(&qp->link_work, ntb_qp_link_work); 123262306a36Sopenharmony_ci INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup_work); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci spin_lock_init(&qp->ntb_rx_q_lock); 123562306a36Sopenharmony_ci spin_lock_init(&qp->ntb_tx_free_q_lock); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci INIT_LIST_HEAD(&qp->rx_post_q); 123862306a36Sopenharmony_ci INIT_LIST_HEAD(&qp->rx_pend_q); 123962306a36Sopenharmony_ci INIT_LIST_HEAD(&qp->rx_free_q); 124062306a36Sopenharmony_ci INIT_LIST_HEAD(&qp->tx_free_q); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci tasklet_init(&qp->rxc_db_work, ntb_transport_rxc_db, 124362306a36Sopenharmony_ci (unsigned long)qp); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct ntb_transport_ctx *nt; 125162306a36Sopenharmony_ci struct ntb_transport_mw *mw; 125262306a36Sopenharmony_ci unsigned int mw_count, qp_count, spad_count, max_mw_count_for_spads; 125362306a36Sopenharmony_ci u64 qp_bitmap; 125462306a36Sopenharmony_ci int node; 125562306a36Sopenharmony_ci int rc, i; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci mw_count = ntb_peer_mw_count(ndev); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (!ndev->ops->mw_set_trans) { 126062306a36Sopenharmony_ci dev_err(&ndev->dev, "Inbound MW based NTB API is required\n"); 126162306a36Sopenharmony_ci return -EINVAL; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (ntb_db_is_unsafe(ndev)) 126562306a36Sopenharmony_ci dev_dbg(&ndev->dev, 126662306a36Sopenharmony_ci "doorbell is unsafe, proceed anyway...\n"); 126762306a36Sopenharmony_ci if (ntb_spad_is_unsafe(ndev)) 126862306a36Sopenharmony_ci dev_dbg(&ndev->dev, 126962306a36Sopenharmony_ci "scratchpad is unsafe, proceed anyway...\n"); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (ntb_peer_port_count(ndev) != NTB_DEF_PEER_CNT) 127262306a36Sopenharmony_ci dev_warn(&ndev->dev, "Multi-port NTB devices unsupported\n"); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci node = dev_to_node(&ndev->dev); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci nt = kzalloc_node(sizeof(*nt), GFP_KERNEL, node); 127762306a36Sopenharmony_ci if (!nt) 127862306a36Sopenharmony_ci return -ENOMEM; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci nt->ndev = ndev; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci /* 128362306a36Sopenharmony_ci * If we are using MSI, and have at least one extra memory window, 128462306a36Sopenharmony_ci * we will reserve the last MW for the MSI window. 128562306a36Sopenharmony_ci */ 128662306a36Sopenharmony_ci if (use_msi && mw_count > 1) { 128762306a36Sopenharmony_ci rc = ntb_msi_init(ndev, ntb_transport_msi_desc_changed); 128862306a36Sopenharmony_ci if (!rc) { 128962306a36Sopenharmony_ci mw_count -= 1; 129062306a36Sopenharmony_ci nt->use_msi = true; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci spad_count = ntb_spad_count(ndev); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Limit the MW's based on the availability of scratchpads */ 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (spad_count < NTB_TRANSPORT_MIN_SPADS) { 129962306a36Sopenharmony_ci nt->mw_count = 0; 130062306a36Sopenharmony_ci rc = -EINVAL; 130162306a36Sopenharmony_ci goto err; 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci max_mw_count_for_spads = (spad_count - MW0_SZ_HIGH) / 2; 130562306a36Sopenharmony_ci nt->mw_count = min(mw_count, max_mw_count_for_spads); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci nt->msi_spad_offset = nt->mw_count * 2 + MW0_SZ_HIGH; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci nt->mw_vec = kcalloc_node(mw_count, sizeof(*nt->mw_vec), 131062306a36Sopenharmony_ci GFP_KERNEL, node); 131162306a36Sopenharmony_ci if (!nt->mw_vec) { 131262306a36Sopenharmony_ci rc = -ENOMEM; 131362306a36Sopenharmony_ci goto err; 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci for (i = 0; i < mw_count; i++) { 131762306a36Sopenharmony_ci mw = &nt->mw_vec[i]; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci rc = ntb_peer_mw_get_addr(ndev, i, &mw->phys_addr, 132062306a36Sopenharmony_ci &mw->phys_size); 132162306a36Sopenharmony_ci if (rc) 132262306a36Sopenharmony_ci goto err1; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci mw->vbase = ioremap_wc(mw->phys_addr, mw->phys_size); 132562306a36Sopenharmony_ci if (!mw->vbase) { 132662306a36Sopenharmony_ci rc = -ENOMEM; 132762306a36Sopenharmony_ci goto err1; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci mw->buff_size = 0; 133162306a36Sopenharmony_ci mw->xlat_size = 0; 133262306a36Sopenharmony_ci mw->virt_addr = NULL; 133362306a36Sopenharmony_ci mw->dma_addr = 0; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci qp_bitmap = ntb_db_valid_mask(ndev); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci qp_count = ilog2(qp_bitmap); 133962306a36Sopenharmony_ci if (nt->use_msi) { 134062306a36Sopenharmony_ci qp_count -= 1; 134162306a36Sopenharmony_ci nt->msi_db_mask = 1 << qp_count; 134262306a36Sopenharmony_ci ntb_db_clear_mask(ndev, nt->msi_db_mask); 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci if (max_num_clients && max_num_clients < qp_count) 134662306a36Sopenharmony_ci qp_count = max_num_clients; 134762306a36Sopenharmony_ci else if (nt->mw_count < qp_count) 134862306a36Sopenharmony_ci qp_count = nt->mw_count; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci qp_bitmap &= BIT_ULL(qp_count) - 1; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci nt->qp_count = qp_count; 135362306a36Sopenharmony_ci nt->qp_bitmap = qp_bitmap; 135462306a36Sopenharmony_ci nt->qp_bitmap_free = qp_bitmap; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci nt->qp_vec = kcalloc_node(qp_count, sizeof(*nt->qp_vec), 135762306a36Sopenharmony_ci GFP_KERNEL, node); 135862306a36Sopenharmony_ci if (!nt->qp_vec) { 135962306a36Sopenharmony_ci rc = -ENOMEM; 136062306a36Sopenharmony_ci goto err1; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (nt_debugfs_dir) { 136462306a36Sopenharmony_ci nt->debugfs_node_dir = 136562306a36Sopenharmony_ci debugfs_create_dir(pci_name(ndev->pdev), 136662306a36Sopenharmony_ci nt_debugfs_dir); 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci for (i = 0; i < qp_count; i++) { 137062306a36Sopenharmony_ci rc = ntb_transport_init_queue(nt, i); 137162306a36Sopenharmony_ci if (rc) 137262306a36Sopenharmony_ci goto err2; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci INIT_DELAYED_WORK(&nt->link_work, ntb_transport_link_work); 137662306a36Sopenharmony_ci INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup_work); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci rc = ntb_set_ctx(ndev, nt, &ntb_transport_ops); 137962306a36Sopenharmony_ci if (rc) 138062306a36Sopenharmony_ci goto err2; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci INIT_LIST_HEAD(&nt->client_devs); 138362306a36Sopenharmony_ci rc = ntb_bus_init(nt); 138462306a36Sopenharmony_ci if (rc) 138562306a36Sopenharmony_ci goto err3; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci nt->link_is_up = false; 138862306a36Sopenharmony_ci ntb_link_enable(ndev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); 138962306a36Sopenharmony_ci ntb_link_event(ndev); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cierr3: 139462306a36Sopenharmony_ci ntb_clear_ctx(ndev); 139562306a36Sopenharmony_cierr2: 139662306a36Sopenharmony_ci kfree(nt->qp_vec); 139762306a36Sopenharmony_cierr1: 139862306a36Sopenharmony_ci while (i--) { 139962306a36Sopenharmony_ci mw = &nt->mw_vec[i]; 140062306a36Sopenharmony_ci iounmap(mw->vbase); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci kfree(nt->mw_vec); 140362306a36Sopenharmony_cierr: 140462306a36Sopenharmony_ci kfree(nt); 140562306a36Sopenharmony_ci return rc; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_cistatic void ntb_transport_free(struct ntb_client *self, struct ntb_dev *ndev) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci struct ntb_transport_ctx *nt = ndev->ctx; 141162306a36Sopenharmony_ci struct ntb_transport_qp *qp; 141262306a36Sopenharmony_ci u64 qp_bitmap_alloc; 141362306a36Sopenharmony_ci int i; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci ntb_transport_link_cleanup(nt); 141662306a36Sopenharmony_ci cancel_work_sync(&nt->link_cleanup); 141762306a36Sopenharmony_ci cancel_delayed_work_sync(&nt->link_work); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci qp_bitmap_alloc = nt->qp_bitmap & ~nt->qp_bitmap_free; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci /* verify that all the qp's are freed */ 142262306a36Sopenharmony_ci for (i = 0; i < nt->qp_count; i++) { 142362306a36Sopenharmony_ci qp = &nt->qp_vec[i]; 142462306a36Sopenharmony_ci if (qp_bitmap_alloc & BIT_ULL(i)) 142562306a36Sopenharmony_ci ntb_transport_free_queue(qp); 142662306a36Sopenharmony_ci debugfs_remove_recursive(qp->debugfs_dir); 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci ntb_link_disable(ndev); 143062306a36Sopenharmony_ci ntb_clear_ctx(ndev); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci ntb_bus_remove(nt); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci for (i = nt->mw_count; i--; ) { 143562306a36Sopenharmony_ci ntb_free_mw(nt, i); 143662306a36Sopenharmony_ci iounmap(nt->mw_vec[i].vbase); 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci kfree(nt->qp_vec); 144062306a36Sopenharmony_ci kfree(nt->mw_vec); 144162306a36Sopenharmony_ci kfree(nt); 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic void ntb_complete_rxc(struct ntb_transport_qp *qp) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci struct ntb_queue_entry *entry; 144762306a36Sopenharmony_ci void *cb_data; 144862306a36Sopenharmony_ci unsigned int len; 144962306a36Sopenharmony_ci unsigned long irqflags; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci spin_lock_irqsave(&qp->ntb_rx_q_lock, irqflags); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci while (!list_empty(&qp->rx_post_q)) { 145462306a36Sopenharmony_ci entry = list_first_entry(&qp->rx_post_q, 145562306a36Sopenharmony_ci struct ntb_queue_entry, entry); 145662306a36Sopenharmony_ci if (!(entry->flags & DESC_DONE_FLAG)) 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci entry->rx_hdr->flags = 0; 146062306a36Sopenharmony_ci iowrite32(entry->rx_index, &qp->rx_info->entry); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci cb_data = entry->cb_data; 146362306a36Sopenharmony_ci len = entry->len; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci list_move_tail(&entry->entry, &qp->rx_free_q); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->ntb_rx_q_lock, irqflags); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (qp->rx_handler && qp->client_ready) 147062306a36Sopenharmony_ci qp->rx_handler(qp, qp->cb_data, cb_data, len); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci spin_lock_irqsave(&qp->ntb_rx_q_lock, irqflags); 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->ntb_rx_q_lock, irqflags); 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic void ntb_rx_copy_callback(void *data, 147962306a36Sopenharmony_ci const struct dmaengine_result *res) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci struct ntb_queue_entry *entry = data; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* we need to check DMA results if we are using DMA */ 148462306a36Sopenharmony_ci if (res) { 148562306a36Sopenharmony_ci enum dmaengine_tx_result dma_err = res->result; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci switch (dma_err) { 148862306a36Sopenharmony_ci case DMA_TRANS_READ_FAILED: 148962306a36Sopenharmony_ci case DMA_TRANS_WRITE_FAILED: 149062306a36Sopenharmony_ci entry->errors++; 149162306a36Sopenharmony_ci fallthrough; 149262306a36Sopenharmony_ci case DMA_TRANS_ABORTED: 149362306a36Sopenharmony_ci { 149462306a36Sopenharmony_ci struct ntb_transport_qp *qp = entry->qp; 149562306a36Sopenharmony_ci void *offset = qp->rx_buff + qp->rx_max_frame * 149662306a36Sopenharmony_ci qp->rx_index; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci ntb_memcpy_rx(entry, offset); 149962306a36Sopenharmony_ci qp->rx_memcpy++; 150062306a36Sopenharmony_ci return; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci case DMA_TRANS_NOERROR: 150462306a36Sopenharmony_ci default: 150562306a36Sopenharmony_ci break; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci entry->flags |= DESC_DONE_FLAG; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci ntb_complete_rxc(entry->qp); 151262306a36Sopenharmony_ci} 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_cistatic void ntb_memcpy_rx(struct ntb_queue_entry *entry, void *offset) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci void *buf = entry->buf; 151762306a36Sopenharmony_ci size_t len = entry->len; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci memcpy(buf, offset, len); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* Ensure that the data is fully copied out before clearing the flag */ 152262306a36Sopenharmony_ci wmb(); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci ntb_rx_copy_callback(entry, NULL); 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic int ntb_async_rx_submit(struct ntb_queue_entry *entry, void *offset) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci struct dma_async_tx_descriptor *txd; 153062306a36Sopenharmony_ci struct ntb_transport_qp *qp = entry->qp; 153162306a36Sopenharmony_ci struct dma_chan *chan = qp->rx_dma_chan; 153262306a36Sopenharmony_ci struct dma_device *device; 153362306a36Sopenharmony_ci size_t pay_off, buff_off, len; 153462306a36Sopenharmony_ci struct dmaengine_unmap_data *unmap; 153562306a36Sopenharmony_ci dma_cookie_t cookie; 153662306a36Sopenharmony_ci void *buf = entry->buf; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci len = entry->len; 153962306a36Sopenharmony_ci device = chan->device; 154062306a36Sopenharmony_ci pay_off = (size_t)offset & ~PAGE_MASK; 154162306a36Sopenharmony_ci buff_off = (size_t)buf & ~PAGE_MASK; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci if (!is_dma_copy_aligned(device, pay_off, buff_off, len)) 154462306a36Sopenharmony_ci goto err; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT); 154762306a36Sopenharmony_ci if (!unmap) 154862306a36Sopenharmony_ci goto err; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci unmap->len = len; 155162306a36Sopenharmony_ci unmap->addr[0] = dma_map_page(device->dev, virt_to_page(offset), 155262306a36Sopenharmony_ci pay_off, len, DMA_TO_DEVICE); 155362306a36Sopenharmony_ci if (dma_mapping_error(device->dev, unmap->addr[0])) 155462306a36Sopenharmony_ci goto err_get_unmap; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci unmap->to_cnt = 1; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci unmap->addr[1] = dma_map_page(device->dev, virt_to_page(buf), 155962306a36Sopenharmony_ci buff_off, len, DMA_FROM_DEVICE); 156062306a36Sopenharmony_ci if (dma_mapping_error(device->dev, unmap->addr[1])) 156162306a36Sopenharmony_ci goto err_get_unmap; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci unmap->from_cnt = 1; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci txd = device->device_prep_dma_memcpy(chan, unmap->addr[1], 156662306a36Sopenharmony_ci unmap->addr[0], len, 156762306a36Sopenharmony_ci DMA_PREP_INTERRUPT); 156862306a36Sopenharmony_ci if (!txd) 156962306a36Sopenharmony_ci goto err_get_unmap; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci txd->callback_result = ntb_rx_copy_callback; 157262306a36Sopenharmony_ci txd->callback_param = entry; 157362306a36Sopenharmony_ci dma_set_unmap(txd, unmap); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci cookie = dmaengine_submit(txd); 157662306a36Sopenharmony_ci if (dma_submit_error(cookie)) 157762306a36Sopenharmony_ci goto err_set_unmap; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci qp->last_cookie = cookie; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci qp->rx_async++; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci return 0; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cierr_set_unmap: 158862306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 158962306a36Sopenharmony_cierr_get_unmap: 159062306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 159162306a36Sopenharmony_cierr: 159262306a36Sopenharmony_ci return -ENXIO; 159362306a36Sopenharmony_ci} 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic void ntb_async_rx(struct ntb_queue_entry *entry, void *offset) 159662306a36Sopenharmony_ci{ 159762306a36Sopenharmony_ci struct ntb_transport_qp *qp = entry->qp; 159862306a36Sopenharmony_ci struct dma_chan *chan = qp->rx_dma_chan; 159962306a36Sopenharmony_ci int res; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci if (!chan) 160262306a36Sopenharmony_ci goto err; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (entry->len < copy_bytes) 160562306a36Sopenharmony_ci goto err; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci res = ntb_async_rx_submit(entry, offset); 160862306a36Sopenharmony_ci if (res < 0) 160962306a36Sopenharmony_ci goto err; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (!entry->retries) 161262306a36Sopenharmony_ci qp->rx_async++; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci return; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cierr: 161762306a36Sopenharmony_ci ntb_memcpy_rx(entry, offset); 161862306a36Sopenharmony_ci qp->rx_memcpy++; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic int ntb_process_rxc(struct ntb_transport_qp *qp) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct ntb_payload_header *hdr; 162462306a36Sopenharmony_ci struct ntb_queue_entry *entry; 162562306a36Sopenharmony_ci void *offset; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci offset = qp->rx_buff + qp->rx_max_frame * qp->rx_index; 162862306a36Sopenharmony_ci hdr = offset + qp->rx_max_frame - sizeof(struct ntb_payload_header); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "qp %d: RX ver %u len %d flags %x\n", 163162306a36Sopenharmony_ci qp->qp_num, hdr->ver, hdr->len, hdr->flags); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (!(hdr->flags & DESC_DONE_FLAG)) { 163462306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "done flag not set\n"); 163562306a36Sopenharmony_ci qp->rx_ring_empty++; 163662306a36Sopenharmony_ci return -EAGAIN; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (hdr->flags & LINK_DOWN_FLAG) { 164062306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "link down flag set\n"); 164162306a36Sopenharmony_ci ntb_qp_link_down(qp); 164262306a36Sopenharmony_ci hdr->flags = 0; 164362306a36Sopenharmony_ci return -EAGAIN; 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci if (hdr->ver != (u32)qp->rx_pkts) { 164762306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, 164862306a36Sopenharmony_ci "version mismatch, expected %llu - got %u\n", 164962306a36Sopenharmony_ci qp->rx_pkts, hdr->ver); 165062306a36Sopenharmony_ci qp->rx_err_ver++; 165162306a36Sopenharmony_ci return -EIO; 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci entry = ntb_list_mv(&qp->ntb_rx_q_lock, &qp->rx_pend_q, &qp->rx_post_q); 165562306a36Sopenharmony_ci if (!entry) { 165662306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "no receive buffer\n"); 165762306a36Sopenharmony_ci qp->rx_err_no_buf++; 165862306a36Sopenharmony_ci return -EAGAIN; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci entry->rx_hdr = hdr; 166262306a36Sopenharmony_ci entry->rx_index = qp->rx_index; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci if (hdr->len > entry->len) { 166562306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, 166662306a36Sopenharmony_ci "receive buffer overflow! Wanted %d got %d\n", 166762306a36Sopenharmony_ci hdr->len, entry->len); 166862306a36Sopenharmony_ci qp->rx_err_oflow++; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci entry->len = -EIO; 167162306a36Sopenharmony_ci entry->flags |= DESC_DONE_FLAG; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci ntb_complete_rxc(qp); 167462306a36Sopenharmony_ci } else { 167562306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, 167662306a36Sopenharmony_ci "RX OK index %u ver %u size %d into buf size %d\n", 167762306a36Sopenharmony_ci qp->rx_index, hdr->ver, hdr->len, entry->len); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci qp->rx_bytes += hdr->len; 168062306a36Sopenharmony_ci qp->rx_pkts++; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci entry->len = hdr->len; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci ntb_async_rx(entry, offset); 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci qp->rx_index++; 168862306a36Sopenharmony_ci qp->rx_index %= qp->rx_max_entry; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci return 0; 169162306a36Sopenharmony_ci} 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cistatic void ntb_transport_rxc_db(unsigned long data) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci struct ntb_transport_qp *qp = (void *)data; 169662306a36Sopenharmony_ci int rc, i; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci dev_dbg(&qp->ndev->pdev->dev, "%s: doorbell %d received\n", 169962306a36Sopenharmony_ci __func__, qp->qp_num); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* Limit the number of packets processed in a single interrupt to 170262306a36Sopenharmony_ci * provide fairness to others 170362306a36Sopenharmony_ci */ 170462306a36Sopenharmony_ci for (i = 0; i < qp->rx_max_entry; i++) { 170562306a36Sopenharmony_ci rc = ntb_process_rxc(qp); 170662306a36Sopenharmony_ci if (rc) 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (i && qp->rx_dma_chan) 171162306a36Sopenharmony_ci dma_async_issue_pending(qp->rx_dma_chan); 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (i == qp->rx_max_entry) { 171462306a36Sopenharmony_ci /* there is more work to do */ 171562306a36Sopenharmony_ci if (qp->active) 171662306a36Sopenharmony_ci tasklet_schedule(&qp->rxc_db_work); 171762306a36Sopenharmony_ci } else if (ntb_db_read(qp->ndev) & BIT_ULL(qp->qp_num)) { 171862306a36Sopenharmony_ci /* the doorbell bit is set: clear it */ 171962306a36Sopenharmony_ci ntb_db_clear(qp->ndev, BIT_ULL(qp->qp_num)); 172062306a36Sopenharmony_ci /* ntb_db_read ensures ntb_db_clear write is committed */ 172162306a36Sopenharmony_ci ntb_db_read(qp->ndev); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci /* an interrupt may have arrived between finishing 172462306a36Sopenharmony_ci * ntb_process_rxc and clearing the doorbell bit: 172562306a36Sopenharmony_ci * there might be some more work to do. 172662306a36Sopenharmony_ci */ 172762306a36Sopenharmony_ci if (qp->active) 172862306a36Sopenharmony_ci tasklet_schedule(&qp->rxc_db_work); 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic void ntb_tx_copy_callback(void *data, 173362306a36Sopenharmony_ci const struct dmaengine_result *res) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci struct ntb_queue_entry *entry = data; 173662306a36Sopenharmony_ci struct ntb_transport_qp *qp = entry->qp; 173762306a36Sopenharmony_ci struct ntb_payload_header __iomem *hdr = entry->tx_hdr; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci /* we need to check DMA results if we are using DMA */ 174062306a36Sopenharmony_ci if (res) { 174162306a36Sopenharmony_ci enum dmaengine_tx_result dma_err = res->result; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci switch (dma_err) { 174462306a36Sopenharmony_ci case DMA_TRANS_READ_FAILED: 174562306a36Sopenharmony_ci case DMA_TRANS_WRITE_FAILED: 174662306a36Sopenharmony_ci entry->errors++; 174762306a36Sopenharmony_ci fallthrough; 174862306a36Sopenharmony_ci case DMA_TRANS_ABORTED: 174962306a36Sopenharmony_ci { 175062306a36Sopenharmony_ci void __iomem *offset = 175162306a36Sopenharmony_ci qp->tx_mw + qp->tx_max_frame * 175262306a36Sopenharmony_ci entry->tx_index; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci /* resubmit via CPU */ 175562306a36Sopenharmony_ci ntb_memcpy_tx(entry, offset); 175662306a36Sopenharmony_ci qp->tx_memcpy++; 175762306a36Sopenharmony_ci return; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci case DMA_TRANS_NOERROR: 176162306a36Sopenharmony_ci default: 176262306a36Sopenharmony_ci break; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci iowrite32(entry->flags | DESC_DONE_FLAG, &hdr->flags); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci if (qp->use_msi) 176962306a36Sopenharmony_ci ntb_msi_peer_trigger(qp->ndev, PIDX, &qp->peer_msi_desc); 177062306a36Sopenharmony_ci else 177162306a36Sopenharmony_ci ntb_peer_db_set(qp->ndev, BIT_ULL(qp->qp_num)); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci /* The entry length can only be zero if the packet is intended to be a 177462306a36Sopenharmony_ci * "link down" or similar. Since no payload is being sent in these 177562306a36Sopenharmony_ci * cases, there is nothing to add to the completion queue. 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ci if (entry->len > 0) { 177862306a36Sopenharmony_ci qp->tx_bytes += entry->len; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci if (qp->tx_handler) 178162306a36Sopenharmony_ci qp->tx_handler(qp, qp->cb_data, entry->cb_data, 178262306a36Sopenharmony_ci entry->len); 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q); 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_cistatic void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci#ifdef ARCH_HAS_NOCACHE_UACCESS 179162306a36Sopenharmony_ci /* 179262306a36Sopenharmony_ci * Using non-temporal mov to improve performance on non-cached 179362306a36Sopenharmony_ci * writes, even though we aren't actually copying from user space. 179462306a36Sopenharmony_ci */ 179562306a36Sopenharmony_ci __copy_from_user_inatomic_nocache(offset, entry->buf, entry->len); 179662306a36Sopenharmony_ci#else 179762306a36Sopenharmony_ci memcpy_toio(offset, entry->buf, entry->len); 179862306a36Sopenharmony_ci#endif 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* Ensure that the data is fully copied out before setting the flags */ 180162306a36Sopenharmony_ci wmb(); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci ntb_tx_copy_callback(entry, NULL); 180462306a36Sopenharmony_ci} 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_cistatic int ntb_async_tx_submit(struct ntb_transport_qp *qp, 180762306a36Sopenharmony_ci struct ntb_queue_entry *entry) 180862306a36Sopenharmony_ci{ 180962306a36Sopenharmony_ci struct dma_async_tx_descriptor *txd; 181062306a36Sopenharmony_ci struct dma_chan *chan = qp->tx_dma_chan; 181162306a36Sopenharmony_ci struct dma_device *device; 181262306a36Sopenharmony_ci size_t len = entry->len; 181362306a36Sopenharmony_ci void *buf = entry->buf; 181462306a36Sopenharmony_ci size_t dest_off, buff_off; 181562306a36Sopenharmony_ci struct dmaengine_unmap_data *unmap; 181662306a36Sopenharmony_ci dma_addr_t dest; 181762306a36Sopenharmony_ci dma_cookie_t cookie; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci device = chan->device; 182062306a36Sopenharmony_ci dest = qp->tx_mw_dma_addr + qp->tx_max_frame * entry->tx_index; 182162306a36Sopenharmony_ci buff_off = (size_t)buf & ~PAGE_MASK; 182262306a36Sopenharmony_ci dest_off = (size_t)dest & ~PAGE_MASK; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci if (!is_dma_copy_aligned(device, buff_off, dest_off, len)) 182562306a36Sopenharmony_ci goto err; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci unmap = dmaengine_get_unmap_data(device->dev, 1, GFP_NOWAIT); 182862306a36Sopenharmony_ci if (!unmap) 182962306a36Sopenharmony_ci goto err; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci unmap->len = len; 183262306a36Sopenharmony_ci unmap->addr[0] = dma_map_page(device->dev, virt_to_page(buf), 183362306a36Sopenharmony_ci buff_off, len, DMA_TO_DEVICE); 183462306a36Sopenharmony_ci if (dma_mapping_error(device->dev, unmap->addr[0])) 183562306a36Sopenharmony_ci goto err_get_unmap; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci unmap->to_cnt = 1; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci txd = device->device_prep_dma_memcpy(chan, dest, unmap->addr[0], len, 184062306a36Sopenharmony_ci DMA_PREP_INTERRUPT); 184162306a36Sopenharmony_ci if (!txd) 184262306a36Sopenharmony_ci goto err_get_unmap; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci txd->callback_result = ntb_tx_copy_callback; 184562306a36Sopenharmony_ci txd->callback_param = entry; 184662306a36Sopenharmony_ci dma_set_unmap(txd, unmap); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci cookie = dmaengine_submit(txd); 184962306a36Sopenharmony_ci if (dma_submit_error(cookie)) 185062306a36Sopenharmony_ci goto err_set_unmap; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci dma_async_issue_pending(chan); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci return 0; 185762306a36Sopenharmony_cierr_set_unmap: 185862306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 185962306a36Sopenharmony_cierr_get_unmap: 186062306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 186162306a36Sopenharmony_cierr: 186262306a36Sopenharmony_ci return -ENXIO; 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_cistatic void ntb_async_tx(struct ntb_transport_qp *qp, 186662306a36Sopenharmony_ci struct ntb_queue_entry *entry) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci struct ntb_payload_header __iomem *hdr; 186962306a36Sopenharmony_ci struct dma_chan *chan = qp->tx_dma_chan; 187062306a36Sopenharmony_ci void __iomem *offset; 187162306a36Sopenharmony_ci int res; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci entry->tx_index = qp->tx_index; 187462306a36Sopenharmony_ci offset = qp->tx_mw + qp->tx_max_frame * entry->tx_index; 187562306a36Sopenharmony_ci hdr = offset + qp->tx_max_frame - sizeof(struct ntb_payload_header); 187662306a36Sopenharmony_ci entry->tx_hdr = hdr; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci iowrite32(entry->len, &hdr->len); 187962306a36Sopenharmony_ci iowrite32((u32)qp->tx_pkts, &hdr->ver); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (!chan) 188262306a36Sopenharmony_ci goto err; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (entry->len < copy_bytes) 188562306a36Sopenharmony_ci goto err; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci res = ntb_async_tx_submit(qp, entry); 188862306a36Sopenharmony_ci if (res < 0) 188962306a36Sopenharmony_ci goto err; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (!entry->retries) 189262306a36Sopenharmony_ci qp->tx_async++; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci return; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cierr: 189762306a36Sopenharmony_ci ntb_memcpy_tx(entry, offset); 189862306a36Sopenharmony_ci qp->tx_memcpy++; 189962306a36Sopenharmony_ci} 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cistatic int ntb_process_tx(struct ntb_transport_qp *qp, 190262306a36Sopenharmony_ci struct ntb_queue_entry *entry) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci if (!ntb_transport_tx_free_entry(qp)) { 190562306a36Sopenharmony_ci qp->tx_ring_full++; 190662306a36Sopenharmony_ci return -EAGAIN; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) { 191062306a36Sopenharmony_ci if (qp->tx_handler) 191162306a36Sopenharmony_ci qp->tx_handler(qp, qp->cb_data, NULL, -EIO); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, 191462306a36Sopenharmony_ci &qp->tx_free_q); 191562306a36Sopenharmony_ci return 0; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci ntb_async_tx(qp, entry); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci qp->tx_index++; 192162306a36Sopenharmony_ci qp->tx_index %= qp->tx_max_entry; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci qp->tx_pkts++; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci return 0; 192662306a36Sopenharmony_ci} 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic void ntb_send_link_down(struct ntb_transport_qp *qp) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci struct pci_dev *pdev = qp->ndev->pdev; 193162306a36Sopenharmony_ci struct ntb_queue_entry *entry; 193262306a36Sopenharmony_ci int i, rc; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci if (!qp->link_is_up) 193562306a36Sopenharmony_ci return; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci dev_info(&pdev->dev, "qp %d: Send Link Down\n", qp->qp_num); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci for (i = 0; i < NTB_LINK_DOWN_TIMEOUT; i++) { 194062306a36Sopenharmony_ci entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); 194162306a36Sopenharmony_ci if (entry) 194262306a36Sopenharmony_ci break; 194362306a36Sopenharmony_ci msleep(100); 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci if (!entry) 194762306a36Sopenharmony_ci return; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci entry->cb_data = NULL; 195062306a36Sopenharmony_ci entry->buf = NULL; 195162306a36Sopenharmony_ci entry->len = 0; 195262306a36Sopenharmony_ci entry->flags = LINK_DOWN_FLAG; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci rc = ntb_process_tx(qp, entry); 195562306a36Sopenharmony_ci if (rc) 195662306a36Sopenharmony_ci dev_err(&pdev->dev, "ntb: QP%d unable to send linkdown msg\n", 195762306a36Sopenharmony_ci qp->qp_num); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci ntb_qp_link_down_reset(qp); 196062306a36Sopenharmony_ci} 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_cistatic bool ntb_dma_filter_fn(struct dma_chan *chan, void *node) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci return dev_to_node(&chan->dev->device) == (int)(unsigned long)node; 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci/** 196862306a36Sopenharmony_ci * ntb_transport_create_queue - Create a new NTB transport layer queue 196962306a36Sopenharmony_ci * @rx_handler: receive callback function 197062306a36Sopenharmony_ci * @tx_handler: transmit callback function 197162306a36Sopenharmony_ci * @event_handler: event callback function 197262306a36Sopenharmony_ci * 197362306a36Sopenharmony_ci * Create a new NTB transport layer queue and provide the queue with a callback 197462306a36Sopenharmony_ci * routine for both transmit and receive. The receive callback routine will be 197562306a36Sopenharmony_ci * used to pass up data when the transport has received it on the queue. The 197662306a36Sopenharmony_ci * transmit callback routine will be called when the transport has completed the 197762306a36Sopenharmony_ci * transmission of the data on the queue and the data is ready to be freed. 197862306a36Sopenharmony_ci * 197962306a36Sopenharmony_ci * RETURNS: pointer to newly created ntb_queue, NULL on error. 198062306a36Sopenharmony_ci */ 198162306a36Sopenharmony_cistruct ntb_transport_qp * 198262306a36Sopenharmony_cintb_transport_create_queue(void *data, struct device *client_dev, 198362306a36Sopenharmony_ci const struct ntb_queue_handlers *handlers) 198462306a36Sopenharmony_ci{ 198562306a36Sopenharmony_ci struct ntb_dev *ndev; 198662306a36Sopenharmony_ci struct pci_dev *pdev; 198762306a36Sopenharmony_ci struct ntb_transport_ctx *nt; 198862306a36Sopenharmony_ci struct ntb_queue_entry *entry; 198962306a36Sopenharmony_ci struct ntb_transport_qp *qp; 199062306a36Sopenharmony_ci u64 qp_bit; 199162306a36Sopenharmony_ci unsigned int free_queue; 199262306a36Sopenharmony_ci dma_cap_mask_t dma_mask; 199362306a36Sopenharmony_ci int node; 199462306a36Sopenharmony_ci int i; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci ndev = dev_ntb(client_dev->parent); 199762306a36Sopenharmony_ci pdev = ndev->pdev; 199862306a36Sopenharmony_ci nt = ndev->ctx; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci node = dev_to_node(&ndev->dev); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci free_queue = ffs(nt->qp_bitmap_free); 200362306a36Sopenharmony_ci if (!free_queue) 200462306a36Sopenharmony_ci goto err; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci /* decrement free_queue to make it zero based */ 200762306a36Sopenharmony_ci free_queue--; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci qp = &nt->qp_vec[free_queue]; 201062306a36Sopenharmony_ci qp_bit = BIT_ULL(qp->qp_num); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci nt->qp_bitmap_free &= ~qp_bit; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci qp->cb_data = data; 201562306a36Sopenharmony_ci qp->rx_handler = handlers->rx_handler; 201662306a36Sopenharmony_ci qp->tx_handler = handlers->tx_handler; 201762306a36Sopenharmony_ci qp->event_handler = handlers->event_handler; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci dma_cap_zero(dma_mask); 202062306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma_mask); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci if (use_dma) { 202362306a36Sopenharmony_ci qp->tx_dma_chan = 202462306a36Sopenharmony_ci dma_request_channel(dma_mask, ntb_dma_filter_fn, 202562306a36Sopenharmony_ci (void *)(unsigned long)node); 202662306a36Sopenharmony_ci if (!qp->tx_dma_chan) 202762306a36Sopenharmony_ci dev_info(&pdev->dev, "Unable to allocate TX DMA channel\n"); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci qp->rx_dma_chan = 203062306a36Sopenharmony_ci dma_request_channel(dma_mask, ntb_dma_filter_fn, 203162306a36Sopenharmony_ci (void *)(unsigned long)node); 203262306a36Sopenharmony_ci if (!qp->rx_dma_chan) 203362306a36Sopenharmony_ci dev_info(&pdev->dev, "Unable to allocate RX DMA channel\n"); 203462306a36Sopenharmony_ci } else { 203562306a36Sopenharmony_ci qp->tx_dma_chan = NULL; 203662306a36Sopenharmony_ci qp->rx_dma_chan = NULL; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci qp->tx_mw_dma_addr = 0; 204062306a36Sopenharmony_ci if (qp->tx_dma_chan) { 204162306a36Sopenharmony_ci qp->tx_mw_dma_addr = 204262306a36Sopenharmony_ci dma_map_resource(qp->tx_dma_chan->device->dev, 204362306a36Sopenharmony_ci qp->tx_mw_phys, qp->tx_mw_size, 204462306a36Sopenharmony_ci DMA_FROM_DEVICE, 0); 204562306a36Sopenharmony_ci if (dma_mapping_error(qp->tx_dma_chan->device->dev, 204662306a36Sopenharmony_ci qp->tx_mw_dma_addr)) { 204762306a36Sopenharmony_ci qp->tx_mw_dma_addr = 0; 204862306a36Sopenharmony_ci goto err1; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Using %s memcpy for TX\n", 205362306a36Sopenharmony_ci qp->tx_dma_chan ? "DMA" : "CPU"); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Using %s memcpy for RX\n", 205662306a36Sopenharmony_ci qp->rx_dma_chan ? "DMA" : "CPU"); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) { 205962306a36Sopenharmony_ci entry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node); 206062306a36Sopenharmony_ci if (!entry) 206162306a36Sopenharmony_ci goto err1; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci entry->qp = qp; 206462306a36Sopenharmony_ci ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, 206562306a36Sopenharmony_ci &qp->rx_free_q); 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci qp->rx_alloc_entry = NTB_QP_DEF_NUM_ENTRIES; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci for (i = 0; i < qp->tx_max_entry; i++) { 207062306a36Sopenharmony_ci entry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node); 207162306a36Sopenharmony_ci if (!entry) 207262306a36Sopenharmony_ci goto err2; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci entry->qp = qp; 207562306a36Sopenharmony_ci ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, 207662306a36Sopenharmony_ci &qp->tx_free_q); 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci ntb_db_clear(qp->ndev, qp_bit); 208062306a36Sopenharmony_ci ntb_db_clear_mask(qp->ndev, qp_bit); 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci dev_info(&pdev->dev, "NTB Transport QP %d created\n", qp->qp_num); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci return qp; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_cierr2: 208762306a36Sopenharmony_ci while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q))) 208862306a36Sopenharmony_ci kfree(entry); 208962306a36Sopenharmony_cierr1: 209062306a36Sopenharmony_ci qp->rx_alloc_entry = 0; 209162306a36Sopenharmony_ci while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) 209262306a36Sopenharmony_ci kfree(entry); 209362306a36Sopenharmony_ci if (qp->tx_mw_dma_addr) 209462306a36Sopenharmony_ci dma_unmap_resource(qp->tx_dma_chan->device->dev, 209562306a36Sopenharmony_ci qp->tx_mw_dma_addr, qp->tx_mw_size, 209662306a36Sopenharmony_ci DMA_FROM_DEVICE, 0); 209762306a36Sopenharmony_ci if (qp->tx_dma_chan) 209862306a36Sopenharmony_ci dma_release_channel(qp->tx_dma_chan); 209962306a36Sopenharmony_ci if (qp->rx_dma_chan) 210062306a36Sopenharmony_ci dma_release_channel(qp->rx_dma_chan); 210162306a36Sopenharmony_ci nt->qp_bitmap_free |= qp_bit; 210262306a36Sopenharmony_cierr: 210362306a36Sopenharmony_ci return NULL; 210462306a36Sopenharmony_ci} 210562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_create_queue); 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci/** 210862306a36Sopenharmony_ci * ntb_transport_free_queue - Frees NTB transport queue 210962306a36Sopenharmony_ci * @qp: NTB queue to be freed 211062306a36Sopenharmony_ci * 211162306a36Sopenharmony_ci * Frees NTB transport queue 211262306a36Sopenharmony_ci */ 211362306a36Sopenharmony_civoid ntb_transport_free_queue(struct ntb_transport_qp *qp) 211462306a36Sopenharmony_ci{ 211562306a36Sopenharmony_ci struct pci_dev *pdev; 211662306a36Sopenharmony_ci struct ntb_queue_entry *entry; 211762306a36Sopenharmony_ci u64 qp_bit; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (!qp) 212062306a36Sopenharmony_ci return; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci pdev = qp->ndev->pdev; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci qp->active = false; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (qp->tx_dma_chan) { 212762306a36Sopenharmony_ci struct dma_chan *chan = qp->tx_dma_chan; 212862306a36Sopenharmony_ci /* Putting the dma_chan to NULL will force any new traffic to be 212962306a36Sopenharmony_ci * processed by the CPU instead of the DAM engine 213062306a36Sopenharmony_ci */ 213162306a36Sopenharmony_ci qp->tx_dma_chan = NULL; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci /* Try to be nice and wait for any queued DMA engine 213462306a36Sopenharmony_ci * transactions to process before smashing it with a rock 213562306a36Sopenharmony_ci */ 213662306a36Sopenharmony_ci dma_sync_wait(chan, qp->last_cookie); 213762306a36Sopenharmony_ci dmaengine_terminate_all(chan); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci dma_unmap_resource(chan->device->dev, 214062306a36Sopenharmony_ci qp->tx_mw_dma_addr, qp->tx_mw_size, 214162306a36Sopenharmony_ci DMA_FROM_DEVICE, 0); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci dma_release_channel(chan); 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (qp->rx_dma_chan) { 214762306a36Sopenharmony_ci struct dma_chan *chan = qp->rx_dma_chan; 214862306a36Sopenharmony_ci /* Putting the dma_chan to NULL will force any new traffic to be 214962306a36Sopenharmony_ci * processed by the CPU instead of the DAM engine 215062306a36Sopenharmony_ci */ 215162306a36Sopenharmony_ci qp->rx_dma_chan = NULL; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci /* Try to be nice and wait for any queued DMA engine 215462306a36Sopenharmony_ci * transactions to process before smashing it with a rock 215562306a36Sopenharmony_ci */ 215662306a36Sopenharmony_ci dma_sync_wait(chan, qp->last_cookie); 215762306a36Sopenharmony_ci dmaengine_terminate_all(chan); 215862306a36Sopenharmony_ci dma_release_channel(chan); 215962306a36Sopenharmony_ci } 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci qp_bit = BIT_ULL(qp->qp_num); 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci ntb_db_set_mask(qp->ndev, qp_bit); 216462306a36Sopenharmony_ci tasklet_kill(&qp->rxc_db_work); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci cancel_delayed_work_sync(&qp->link_work); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci qp->cb_data = NULL; 216962306a36Sopenharmony_ci qp->rx_handler = NULL; 217062306a36Sopenharmony_ci qp->tx_handler = NULL; 217162306a36Sopenharmony_ci qp->event_handler = NULL; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) 217462306a36Sopenharmony_ci kfree(entry); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_pend_q))) { 217762306a36Sopenharmony_ci dev_warn(&pdev->dev, "Freeing item from non-empty rx_pend_q\n"); 217862306a36Sopenharmony_ci kfree(entry); 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_post_q))) { 218262306a36Sopenharmony_ci dev_warn(&pdev->dev, "Freeing item from non-empty rx_post_q\n"); 218362306a36Sopenharmony_ci kfree(entry); 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q))) 218762306a36Sopenharmony_ci kfree(entry); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci qp->transport->qp_bitmap_free |= qp_bit; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci dev_info(&pdev->dev, "NTB Transport QP %d freed\n", qp->qp_num); 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_free_queue); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci/** 219662306a36Sopenharmony_ci * ntb_transport_rx_remove - Dequeues enqueued rx packet 219762306a36Sopenharmony_ci * @qp: NTB queue to be freed 219862306a36Sopenharmony_ci * @len: pointer to variable to write enqueued buffers length 219962306a36Sopenharmony_ci * 220062306a36Sopenharmony_ci * Dequeues unused buffers from receive queue. Should only be used during 220162306a36Sopenharmony_ci * shutdown of qp. 220262306a36Sopenharmony_ci * 220362306a36Sopenharmony_ci * RETURNS: NULL error value on error, or void* for success. 220462306a36Sopenharmony_ci */ 220562306a36Sopenharmony_civoid *ntb_transport_rx_remove(struct ntb_transport_qp *qp, unsigned int *len) 220662306a36Sopenharmony_ci{ 220762306a36Sopenharmony_ci struct ntb_queue_entry *entry; 220862306a36Sopenharmony_ci void *buf; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci if (!qp || qp->client_ready) 221162306a36Sopenharmony_ci return NULL; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_pend_q); 221462306a36Sopenharmony_ci if (!entry) 221562306a36Sopenharmony_ci return NULL; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci buf = entry->cb_data; 221862306a36Sopenharmony_ci *len = entry->len; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_free_q); 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci return buf; 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_rx_remove); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci/** 222762306a36Sopenharmony_ci * ntb_transport_rx_enqueue - Enqueue a new NTB queue entry 222862306a36Sopenharmony_ci * @qp: NTB transport layer queue the entry is to be enqueued on 222962306a36Sopenharmony_ci * @cb: per buffer pointer for callback function to use 223062306a36Sopenharmony_ci * @data: pointer to data buffer that incoming packets will be copied into 223162306a36Sopenharmony_ci * @len: length of the data buffer 223262306a36Sopenharmony_ci * 223362306a36Sopenharmony_ci * Enqueue a new receive buffer onto the transport queue into which a NTB 223462306a36Sopenharmony_ci * payload can be received into. 223562306a36Sopenharmony_ci * 223662306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success. 223762306a36Sopenharmony_ci */ 223862306a36Sopenharmony_ciint ntb_transport_rx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, 223962306a36Sopenharmony_ci unsigned int len) 224062306a36Sopenharmony_ci{ 224162306a36Sopenharmony_ci struct ntb_queue_entry *entry; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if (!qp) 224462306a36Sopenharmony_ci return -EINVAL; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q); 224762306a36Sopenharmony_ci if (!entry) 224862306a36Sopenharmony_ci return -ENOMEM; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci entry->cb_data = cb; 225162306a36Sopenharmony_ci entry->buf = data; 225262306a36Sopenharmony_ci entry->len = len; 225362306a36Sopenharmony_ci entry->flags = 0; 225462306a36Sopenharmony_ci entry->retries = 0; 225562306a36Sopenharmony_ci entry->errors = 0; 225662306a36Sopenharmony_ci entry->rx_index = 0; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_pend_q); 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci if (qp->active) 226162306a36Sopenharmony_ci tasklet_schedule(&qp->rxc_db_work); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci return 0; 226462306a36Sopenharmony_ci} 226562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_rx_enqueue); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci/** 226862306a36Sopenharmony_ci * ntb_transport_tx_enqueue - Enqueue a new NTB queue entry 226962306a36Sopenharmony_ci * @qp: NTB transport layer queue the entry is to be enqueued on 227062306a36Sopenharmony_ci * @cb: per buffer pointer for callback function to use 227162306a36Sopenharmony_ci * @data: pointer to data buffer that will be sent 227262306a36Sopenharmony_ci * @len: length of the data buffer 227362306a36Sopenharmony_ci * 227462306a36Sopenharmony_ci * Enqueue a new transmit buffer onto the transport queue from which a NTB 227562306a36Sopenharmony_ci * payload will be transmitted. This assumes that a lock is being held to 227662306a36Sopenharmony_ci * serialize access to the qp. 227762306a36Sopenharmony_ci * 227862306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success. 227962306a36Sopenharmony_ci */ 228062306a36Sopenharmony_ciint ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, 228162306a36Sopenharmony_ci unsigned int len) 228262306a36Sopenharmony_ci{ 228362306a36Sopenharmony_ci struct ntb_queue_entry *entry; 228462306a36Sopenharmony_ci int rc; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci if (!qp || !len) 228762306a36Sopenharmony_ci return -EINVAL; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci /* If the qp link is down already, just ignore. */ 229062306a36Sopenharmony_ci if (!qp->link_is_up) 229162306a36Sopenharmony_ci return 0; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); 229462306a36Sopenharmony_ci if (!entry) { 229562306a36Sopenharmony_ci qp->tx_err_no_buf++; 229662306a36Sopenharmony_ci return -EBUSY; 229762306a36Sopenharmony_ci } 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci entry->cb_data = cb; 230062306a36Sopenharmony_ci entry->buf = data; 230162306a36Sopenharmony_ci entry->len = len; 230262306a36Sopenharmony_ci entry->flags = 0; 230362306a36Sopenharmony_ci entry->errors = 0; 230462306a36Sopenharmony_ci entry->retries = 0; 230562306a36Sopenharmony_ci entry->tx_index = 0; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci rc = ntb_process_tx(qp, entry); 230862306a36Sopenharmony_ci if (rc) 230962306a36Sopenharmony_ci ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, 231062306a36Sopenharmony_ci &qp->tx_free_q); 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci return rc; 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_tx_enqueue); 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci/** 231762306a36Sopenharmony_ci * ntb_transport_link_up - Notify NTB transport of client readiness to use queue 231862306a36Sopenharmony_ci * @qp: NTB transport layer queue to be enabled 231962306a36Sopenharmony_ci * 232062306a36Sopenharmony_ci * Notify NTB transport layer of client readiness to use queue 232162306a36Sopenharmony_ci */ 232262306a36Sopenharmony_civoid ntb_transport_link_up(struct ntb_transport_qp *qp) 232362306a36Sopenharmony_ci{ 232462306a36Sopenharmony_ci if (!qp) 232562306a36Sopenharmony_ci return; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci qp->client_ready = true; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci if (qp->transport->link_is_up) 233062306a36Sopenharmony_ci schedule_delayed_work(&qp->link_work, 0); 233162306a36Sopenharmony_ci} 233262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_link_up); 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci/** 233562306a36Sopenharmony_ci * ntb_transport_link_down - Notify NTB transport to no longer enqueue data 233662306a36Sopenharmony_ci * @qp: NTB transport layer queue to be disabled 233762306a36Sopenharmony_ci * 233862306a36Sopenharmony_ci * Notify NTB transport layer of client's desire to no longer receive data on 233962306a36Sopenharmony_ci * transport queue specified. It is the client's responsibility to ensure all 234062306a36Sopenharmony_ci * entries on queue are purged or otherwise handled appropriately. 234162306a36Sopenharmony_ci */ 234262306a36Sopenharmony_civoid ntb_transport_link_down(struct ntb_transport_qp *qp) 234362306a36Sopenharmony_ci{ 234462306a36Sopenharmony_ci int val; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci if (!qp) 234762306a36Sopenharmony_ci return; 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci qp->client_ready = false; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci val = ntb_spad_read(qp->ndev, QP_LINKS); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci ntb_peer_spad_write(qp->ndev, PIDX, QP_LINKS, val & ~BIT(qp->qp_num)); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci if (qp->link_is_up) 235662306a36Sopenharmony_ci ntb_send_link_down(qp); 235762306a36Sopenharmony_ci else 235862306a36Sopenharmony_ci cancel_delayed_work_sync(&qp->link_work); 235962306a36Sopenharmony_ci} 236062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_link_down); 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci/** 236362306a36Sopenharmony_ci * ntb_transport_link_query - Query transport link state 236462306a36Sopenharmony_ci * @qp: NTB transport layer queue to be queried 236562306a36Sopenharmony_ci * 236662306a36Sopenharmony_ci * Query connectivity to the remote system of the NTB transport queue 236762306a36Sopenharmony_ci * 236862306a36Sopenharmony_ci * RETURNS: true for link up or false for link down 236962306a36Sopenharmony_ci */ 237062306a36Sopenharmony_cibool ntb_transport_link_query(struct ntb_transport_qp *qp) 237162306a36Sopenharmony_ci{ 237262306a36Sopenharmony_ci if (!qp) 237362306a36Sopenharmony_ci return false; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci return qp->link_is_up; 237662306a36Sopenharmony_ci} 237762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_link_query); 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci/** 238062306a36Sopenharmony_ci * ntb_transport_qp_num - Query the qp number 238162306a36Sopenharmony_ci * @qp: NTB transport layer queue to be queried 238262306a36Sopenharmony_ci * 238362306a36Sopenharmony_ci * Query qp number of the NTB transport queue 238462306a36Sopenharmony_ci * 238562306a36Sopenharmony_ci * RETURNS: a zero based number specifying the qp number 238662306a36Sopenharmony_ci */ 238762306a36Sopenharmony_ciunsigned char ntb_transport_qp_num(struct ntb_transport_qp *qp) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ci if (!qp) 239062306a36Sopenharmony_ci return 0; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci return qp->qp_num; 239362306a36Sopenharmony_ci} 239462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_qp_num); 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci/** 239762306a36Sopenharmony_ci * ntb_transport_max_size - Query the max payload size of a qp 239862306a36Sopenharmony_ci * @qp: NTB transport layer queue to be queried 239962306a36Sopenharmony_ci * 240062306a36Sopenharmony_ci * Query the maximum payload size permissible on the given qp 240162306a36Sopenharmony_ci * 240262306a36Sopenharmony_ci * RETURNS: the max payload size of a qp 240362306a36Sopenharmony_ci */ 240462306a36Sopenharmony_ciunsigned int ntb_transport_max_size(struct ntb_transport_qp *qp) 240562306a36Sopenharmony_ci{ 240662306a36Sopenharmony_ci unsigned int max_size; 240762306a36Sopenharmony_ci unsigned int copy_align; 240862306a36Sopenharmony_ci struct dma_chan *rx_chan, *tx_chan; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci if (!qp) 241162306a36Sopenharmony_ci return 0; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci rx_chan = qp->rx_dma_chan; 241462306a36Sopenharmony_ci tx_chan = qp->tx_dma_chan; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci copy_align = max(rx_chan ? rx_chan->device->copy_align : 0, 241762306a36Sopenharmony_ci tx_chan ? tx_chan->device->copy_align : 0); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci /* If DMA engine usage is possible, try to find the max size for that */ 242062306a36Sopenharmony_ci max_size = qp->tx_max_frame - sizeof(struct ntb_payload_header); 242162306a36Sopenharmony_ci max_size = round_down(max_size, 1 << copy_align); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci return max_size; 242462306a36Sopenharmony_ci} 242562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_max_size); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ciunsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp) 242862306a36Sopenharmony_ci{ 242962306a36Sopenharmony_ci unsigned int head = qp->tx_index; 243062306a36Sopenharmony_ci unsigned int tail = qp->remote_rx_info->entry; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci return tail >= head ? tail - head : qp->tx_max_entry + tail - head; 243362306a36Sopenharmony_ci} 243462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_tx_free_entry); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_cistatic void ntb_transport_doorbell_callback(void *data, int vector) 243762306a36Sopenharmony_ci{ 243862306a36Sopenharmony_ci struct ntb_transport_ctx *nt = data; 243962306a36Sopenharmony_ci struct ntb_transport_qp *qp; 244062306a36Sopenharmony_ci u64 db_bits; 244162306a36Sopenharmony_ci unsigned int qp_num; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci if (ntb_db_read(nt->ndev) & nt->msi_db_mask) { 244462306a36Sopenharmony_ci ntb_transport_msi_peer_desc_changed(nt); 244562306a36Sopenharmony_ci ntb_db_clear(nt->ndev, nt->msi_db_mask); 244662306a36Sopenharmony_ci } 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci db_bits = (nt->qp_bitmap & ~nt->qp_bitmap_free & 244962306a36Sopenharmony_ci ntb_db_vector_mask(nt->ndev, vector)); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci while (db_bits) { 245262306a36Sopenharmony_ci qp_num = __ffs(db_bits); 245362306a36Sopenharmony_ci qp = &nt->qp_vec[qp_num]; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci if (qp->active) 245662306a36Sopenharmony_ci tasklet_schedule(&qp->rxc_db_work); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci db_bits &= ~BIT_ULL(qp_num); 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci} 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_cistatic const struct ntb_ctx_ops ntb_transport_ops = { 246362306a36Sopenharmony_ci .link_event = ntb_transport_event_callback, 246462306a36Sopenharmony_ci .db_event = ntb_transport_doorbell_callback, 246562306a36Sopenharmony_ci}; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_cistatic struct ntb_client ntb_transport_client = { 246862306a36Sopenharmony_ci .ops = { 246962306a36Sopenharmony_ci .probe = ntb_transport_probe, 247062306a36Sopenharmony_ci .remove = ntb_transport_free, 247162306a36Sopenharmony_ci }, 247262306a36Sopenharmony_ci}; 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_cistatic int __init ntb_transport_init(void) 247562306a36Sopenharmony_ci{ 247662306a36Sopenharmony_ci int rc; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci pr_info("%s, version %s\n", NTB_TRANSPORT_DESC, NTB_TRANSPORT_VER); 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci if (debugfs_initialized()) 248162306a36Sopenharmony_ci nt_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci rc = bus_register(&ntb_transport_bus); 248462306a36Sopenharmony_ci if (rc) 248562306a36Sopenharmony_ci goto err_bus; 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci rc = ntb_register_client(&ntb_transport_client); 248862306a36Sopenharmony_ci if (rc) 248962306a36Sopenharmony_ci goto err_client; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci return 0; 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_cierr_client: 249462306a36Sopenharmony_ci bus_unregister(&ntb_transport_bus); 249562306a36Sopenharmony_cierr_bus: 249662306a36Sopenharmony_ci debugfs_remove_recursive(nt_debugfs_dir); 249762306a36Sopenharmony_ci return rc; 249862306a36Sopenharmony_ci} 249962306a36Sopenharmony_cimodule_init(ntb_transport_init); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_cistatic void __exit ntb_transport_exit(void) 250262306a36Sopenharmony_ci{ 250362306a36Sopenharmony_ci ntb_unregister_client(&ntb_transport_client); 250462306a36Sopenharmony_ci bus_unregister(&ntb_transport_bus); 250562306a36Sopenharmony_ci debugfs_remove_recursive(nt_debugfs_dir); 250662306a36Sopenharmony_ci} 250762306a36Sopenharmony_cimodule_exit(ntb_transport_exit); 2508