162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DMA traffic test driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020, Intel Corporation 662306a36Sopenharmony_ci * Authors: Isaac Hazan <isaac.hazan@intel.com> 762306a36Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/completion.h> 1162306a36Sopenharmony_ci#include <linux/debugfs.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/sizes.h> 1462306a36Sopenharmony_ci#include <linux/thunderbolt.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DMA_TEST_TX_RING_SIZE 64 1762306a36Sopenharmony_ci#define DMA_TEST_RX_RING_SIZE 256 1862306a36Sopenharmony_ci#define DMA_TEST_FRAME_SIZE SZ_4K 1962306a36Sopenharmony_ci#define DMA_TEST_DATA_PATTERN 0x0123456789abcdefLL 2062306a36Sopenharmony_ci#define DMA_TEST_MAX_PACKETS 1000 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cienum dma_test_frame_pdf { 2362306a36Sopenharmony_ci DMA_TEST_PDF_FRAME_START = 1, 2462306a36Sopenharmony_ci DMA_TEST_PDF_FRAME_END, 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct dma_test_frame { 2862306a36Sopenharmony_ci struct dma_test *dma_test; 2962306a36Sopenharmony_ci void *data; 3062306a36Sopenharmony_ci struct ring_frame frame; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cienum dma_test_test_error { 3462306a36Sopenharmony_ci DMA_TEST_NO_ERROR, 3562306a36Sopenharmony_ci DMA_TEST_INTERRUPTED, 3662306a36Sopenharmony_ci DMA_TEST_BUFFER_ERROR, 3762306a36Sopenharmony_ci DMA_TEST_DMA_ERROR, 3862306a36Sopenharmony_ci DMA_TEST_CONFIG_ERROR, 3962306a36Sopenharmony_ci DMA_TEST_SPEED_ERROR, 4062306a36Sopenharmony_ci DMA_TEST_WIDTH_ERROR, 4162306a36Sopenharmony_ci DMA_TEST_BONDING_ERROR, 4262306a36Sopenharmony_ci DMA_TEST_PACKET_ERROR, 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const char * const dma_test_error_names[] = { 4662306a36Sopenharmony_ci [DMA_TEST_NO_ERROR] = "no errors", 4762306a36Sopenharmony_ci [DMA_TEST_INTERRUPTED] = "interrupted by signal", 4862306a36Sopenharmony_ci [DMA_TEST_BUFFER_ERROR] = "no memory for packet buffers", 4962306a36Sopenharmony_ci [DMA_TEST_DMA_ERROR] = "DMA ring setup failed", 5062306a36Sopenharmony_ci [DMA_TEST_CONFIG_ERROR] = "configuration is not valid", 5162306a36Sopenharmony_ci [DMA_TEST_SPEED_ERROR] = "unexpected link speed", 5262306a36Sopenharmony_ci [DMA_TEST_WIDTH_ERROR] = "unexpected link width", 5362306a36Sopenharmony_ci [DMA_TEST_BONDING_ERROR] = "lane bonding configuration error", 5462306a36Sopenharmony_ci [DMA_TEST_PACKET_ERROR] = "packet check failed", 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cienum dma_test_result { 5862306a36Sopenharmony_ci DMA_TEST_NOT_RUN, 5962306a36Sopenharmony_ci DMA_TEST_SUCCESS, 6062306a36Sopenharmony_ci DMA_TEST_FAIL, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const char * const dma_test_result_names[] = { 6462306a36Sopenharmony_ci [DMA_TEST_NOT_RUN] = "not run", 6562306a36Sopenharmony_ci [DMA_TEST_SUCCESS] = "success", 6662306a36Sopenharmony_ci [DMA_TEST_FAIL] = "failed", 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * struct dma_test - DMA test device driver private data 7162306a36Sopenharmony_ci * @svc: XDomain service the driver is bound to 7262306a36Sopenharmony_ci * @xd: XDomain the service belongs to 7362306a36Sopenharmony_ci * @rx_ring: Software ring holding RX frames 7462306a36Sopenharmony_ci * @rx_hopid: HopID used for receiving frames 7562306a36Sopenharmony_ci * @tx_ring: Software ring holding TX frames 7662306a36Sopenharmony_ci * @tx_hopid: HopID used for sending fames 7762306a36Sopenharmony_ci * @packets_to_send: Number of packets to send 7862306a36Sopenharmony_ci * @packets_to_receive: Number of packets to receive 7962306a36Sopenharmony_ci * @packets_sent: Actual number of packets sent 8062306a36Sopenharmony_ci * @packets_received: Actual number of packets received 8162306a36Sopenharmony_ci * @link_speed: Expected link speed (Gb/s), %0 to use whatever is negotiated 8262306a36Sopenharmony_ci * @link_width: Expected link width (Gb/s), %0 to use whatever is negotiated 8362306a36Sopenharmony_ci * @crc_errors: Number of CRC errors during the test run 8462306a36Sopenharmony_ci * @buffer_overflow_errors: Number of buffer overflow errors during the test 8562306a36Sopenharmony_ci * run 8662306a36Sopenharmony_ci * @result: Result of the last run 8762306a36Sopenharmony_ci * @error_code: Error code of the last run 8862306a36Sopenharmony_ci * @complete: Used to wait for the Rx to complete 8962306a36Sopenharmony_ci * @lock: Lock serializing access to this structure 9062306a36Sopenharmony_ci * @debugfs_dir: dentry of this dma_test 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistruct dma_test { 9362306a36Sopenharmony_ci const struct tb_service *svc; 9462306a36Sopenharmony_ci struct tb_xdomain *xd; 9562306a36Sopenharmony_ci struct tb_ring *rx_ring; 9662306a36Sopenharmony_ci int rx_hopid; 9762306a36Sopenharmony_ci struct tb_ring *tx_ring; 9862306a36Sopenharmony_ci int tx_hopid; 9962306a36Sopenharmony_ci unsigned int packets_to_send; 10062306a36Sopenharmony_ci unsigned int packets_to_receive; 10162306a36Sopenharmony_ci unsigned int packets_sent; 10262306a36Sopenharmony_ci unsigned int packets_received; 10362306a36Sopenharmony_ci unsigned int link_speed; 10462306a36Sopenharmony_ci unsigned int link_width; 10562306a36Sopenharmony_ci unsigned int crc_errors; 10662306a36Sopenharmony_ci unsigned int buffer_overflow_errors; 10762306a36Sopenharmony_ci enum dma_test_result result; 10862306a36Sopenharmony_ci enum dma_test_test_error error_code; 10962306a36Sopenharmony_ci struct completion complete; 11062306a36Sopenharmony_ci struct mutex lock; 11162306a36Sopenharmony_ci struct dentry *debugfs_dir; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* DMA test property directory UUID: 3188cd10-6523-4a5a-a682-fdca07a248d8 */ 11562306a36Sopenharmony_cistatic const uuid_t dma_test_dir_uuid = 11662306a36Sopenharmony_ci UUID_INIT(0x3188cd10, 0x6523, 0x4a5a, 11762306a36Sopenharmony_ci 0xa6, 0x82, 0xfd, 0xca, 0x07, 0xa2, 0x48, 0xd8); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct tb_property_dir *dma_test_dir; 12062306a36Sopenharmony_cistatic void *dma_test_pattern; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void dma_test_free_rings(struct dma_test *dt) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci if (dt->rx_ring) { 12562306a36Sopenharmony_ci tb_xdomain_release_in_hopid(dt->xd, dt->rx_hopid); 12662306a36Sopenharmony_ci tb_ring_free(dt->rx_ring); 12762306a36Sopenharmony_ci dt->rx_ring = NULL; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci if (dt->tx_ring) { 13062306a36Sopenharmony_ci tb_xdomain_release_out_hopid(dt->xd, dt->tx_hopid); 13162306a36Sopenharmony_ci tb_ring_free(dt->tx_ring); 13262306a36Sopenharmony_ci dt->tx_ring = NULL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int dma_test_start_rings(struct dma_test *dt) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci unsigned int flags = RING_FLAG_FRAME; 13962306a36Sopenharmony_ci struct tb_xdomain *xd = dt->xd; 14062306a36Sopenharmony_ci int ret, e2e_tx_hop = 0; 14162306a36Sopenharmony_ci struct tb_ring *ring; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * If we are both sender and receiver (traffic goes over a 14562306a36Sopenharmony_ci * special loopback dongle) enable E2E flow control. This avoids 14662306a36Sopenharmony_ci * losing packets. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci if (dt->packets_to_send && dt->packets_to_receive) 14962306a36Sopenharmony_ci flags |= RING_FLAG_E2E; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (dt->packets_to_send) { 15262306a36Sopenharmony_ci ring = tb_ring_alloc_tx(xd->tb->nhi, -1, DMA_TEST_TX_RING_SIZE, 15362306a36Sopenharmony_ci flags); 15462306a36Sopenharmony_ci if (!ring) 15562306a36Sopenharmony_ci return -ENOMEM; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci dt->tx_ring = ring; 15862306a36Sopenharmony_ci e2e_tx_hop = ring->hop; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ret = tb_xdomain_alloc_out_hopid(xd, -1); 16162306a36Sopenharmony_ci if (ret < 0) { 16262306a36Sopenharmony_ci dma_test_free_rings(dt); 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci dt->tx_hopid = ret; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (dt->packets_to_receive) { 17062306a36Sopenharmony_ci u16 sof_mask, eof_mask; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci sof_mask = BIT(DMA_TEST_PDF_FRAME_START); 17362306a36Sopenharmony_ci eof_mask = BIT(DMA_TEST_PDF_FRAME_END); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ring = tb_ring_alloc_rx(xd->tb->nhi, -1, DMA_TEST_RX_RING_SIZE, 17662306a36Sopenharmony_ci flags, e2e_tx_hop, sof_mask, eof_mask, 17762306a36Sopenharmony_ci NULL, NULL); 17862306a36Sopenharmony_ci if (!ring) { 17962306a36Sopenharmony_ci dma_test_free_rings(dt); 18062306a36Sopenharmony_ci return -ENOMEM; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci dt->rx_ring = ring; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = tb_xdomain_alloc_in_hopid(xd, -1); 18662306a36Sopenharmony_ci if (ret < 0) { 18762306a36Sopenharmony_ci dma_test_free_rings(dt); 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci dt->rx_hopid = ret; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ret = tb_xdomain_enable_paths(dt->xd, dt->tx_hopid, 19562306a36Sopenharmony_ci dt->tx_ring ? dt->tx_ring->hop : -1, 19662306a36Sopenharmony_ci dt->rx_hopid, 19762306a36Sopenharmony_ci dt->rx_ring ? dt->rx_ring->hop : -1); 19862306a36Sopenharmony_ci if (ret) { 19962306a36Sopenharmony_ci dma_test_free_rings(dt); 20062306a36Sopenharmony_ci return ret; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (dt->tx_ring) 20462306a36Sopenharmony_ci tb_ring_start(dt->tx_ring); 20562306a36Sopenharmony_ci if (dt->rx_ring) 20662306a36Sopenharmony_ci tb_ring_start(dt->rx_ring); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void dma_test_stop_rings(struct dma_test *dt) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (dt->rx_ring) 21662306a36Sopenharmony_ci tb_ring_stop(dt->rx_ring); 21762306a36Sopenharmony_ci if (dt->tx_ring) 21862306a36Sopenharmony_ci tb_ring_stop(dt->tx_ring); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = tb_xdomain_disable_paths(dt->xd, dt->tx_hopid, 22162306a36Sopenharmony_ci dt->tx_ring ? dt->tx_ring->hop : -1, 22262306a36Sopenharmony_ci dt->rx_hopid, 22362306a36Sopenharmony_ci dt->rx_ring ? dt->rx_ring->hop : -1); 22462306a36Sopenharmony_ci if (ret) 22562306a36Sopenharmony_ci dev_warn(&dt->svc->dev, "failed to disable DMA paths\n"); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci dma_test_free_rings(dt); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void dma_test_rx_callback(struct tb_ring *ring, struct ring_frame *frame, 23162306a36Sopenharmony_ci bool canceled) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame); 23462306a36Sopenharmony_ci struct dma_test *dt = tf->dma_test; 23562306a36Sopenharmony_ci struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE, 23862306a36Sopenharmony_ci DMA_FROM_DEVICE); 23962306a36Sopenharmony_ci kfree(tf->data); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (canceled) { 24262306a36Sopenharmony_ci kfree(tf); 24362306a36Sopenharmony_ci return; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dt->packets_received++; 24762306a36Sopenharmony_ci dev_dbg(&dt->svc->dev, "packet %u/%u received\n", dt->packets_received, 24862306a36Sopenharmony_ci dt->packets_to_receive); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (tf->frame.flags & RING_DESC_CRC_ERROR) 25162306a36Sopenharmony_ci dt->crc_errors++; 25262306a36Sopenharmony_ci if (tf->frame.flags & RING_DESC_BUFFER_OVERRUN) 25362306a36Sopenharmony_ci dt->buffer_overflow_errors++; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci kfree(tf); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (dt->packets_received == dt->packets_to_receive) 25862306a36Sopenharmony_ci complete(&dt->complete); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int dma_test_submit_rx(struct dma_test *dt, size_t npackets) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); 26462306a36Sopenharmony_ci int i; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci for (i = 0; i < npackets; i++) { 26762306a36Sopenharmony_ci struct dma_test_frame *tf; 26862306a36Sopenharmony_ci dma_addr_t dma_addr; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci tf = kzalloc(sizeof(*tf), GFP_KERNEL); 27162306a36Sopenharmony_ci if (!tf) 27262306a36Sopenharmony_ci return -ENOMEM; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); 27562306a36Sopenharmony_ci if (!tf->data) { 27662306a36Sopenharmony_ci kfree(tf); 27762306a36Sopenharmony_ci return -ENOMEM; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE, 28162306a36Sopenharmony_ci DMA_FROM_DEVICE); 28262306a36Sopenharmony_ci if (dma_mapping_error(dma_dev, dma_addr)) { 28362306a36Sopenharmony_ci kfree(tf->data); 28462306a36Sopenharmony_ci kfree(tf); 28562306a36Sopenharmony_ci return -ENOMEM; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci tf->frame.buffer_phy = dma_addr; 28962306a36Sopenharmony_ci tf->frame.callback = dma_test_rx_callback; 29062306a36Sopenharmony_ci tf->dma_test = dt; 29162306a36Sopenharmony_ci INIT_LIST_HEAD(&tf->frame.list); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci tb_ring_rx(dt->rx_ring, &tf->frame); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void dma_test_tx_callback(struct tb_ring *ring, struct ring_frame *frame, 30062306a36Sopenharmony_ci bool canceled) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame); 30362306a36Sopenharmony_ci struct dma_test *dt = tf->dma_test; 30462306a36Sopenharmony_ci struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE, 30762306a36Sopenharmony_ci DMA_TO_DEVICE); 30862306a36Sopenharmony_ci kfree(tf->data); 30962306a36Sopenharmony_ci kfree(tf); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int dma_test_submit_tx(struct dma_test *dt, size_t npackets) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci for (i = 0; i < npackets; i++) { 31862306a36Sopenharmony_ci struct dma_test_frame *tf; 31962306a36Sopenharmony_ci dma_addr_t dma_addr; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci tf = kzalloc(sizeof(*tf), GFP_KERNEL); 32262306a36Sopenharmony_ci if (!tf) 32362306a36Sopenharmony_ci return -ENOMEM; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci tf->frame.size = 0; /* means 4096 */ 32662306a36Sopenharmony_ci tf->dma_test = dt; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci tf->data = kmemdup(dma_test_pattern, DMA_TEST_FRAME_SIZE, GFP_KERNEL); 32962306a36Sopenharmony_ci if (!tf->data) { 33062306a36Sopenharmony_ci kfree(tf); 33162306a36Sopenharmony_ci return -ENOMEM; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE, 33562306a36Sopenharmony_ci DMA_TO_DEVICE); 33662306a36Sopenharmony_ci if (dma_mapping_error(dma_dev, dma_addr)) { 33762306a36Sopenharmony_ci kfree(tf->data); 33862306a36Sopenharmony_ci kfree(tf); 33962306a36Sopenharmony_ci return -ENOMEM; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci tf->frame.buffer_phy = dma_addr; 34362306a36Sopenharmony_ci tf->frame.callback = dma_test_tx_callback; 34462306a36Sopenharmony_ci tf->frame.sof = DMA_TEST_PDF_FRAME_START; 34562306a36Sopenharmony_ci tf->frame.eof = DMA_TEST_PDF_FRAME_END; 34662306a36Sopenharmony_ci INIT_LIST_HEAD(&tf->frame.list); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci dt->packets_sent++; 34962306a36Sopenharmony_ci dev_dbg(&dt->svc->dev, "packet %u/%u sent\n", dt->packets_sent, 35062306a36Sopenharmony_ci dt->packets_to_send); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci tb_ring_tx(dt->tx_ring, &tf->frame); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci#define DMA_TEST_DEBUGFS_ATTR(__fops, __get, __validate, __set) \ 35962306a36Sopenharmony_cistatic int __fops ## _show(void *data, u64 *val) \ 36062306a36Sopenharmony_ci{ \ 36162306a36Sopenharmony_ci struct tb_service *svc = data; \ 36262306a36Sopenharmony_ci struct dma_test *dt = tb_service_get_drvdata(svc); \ 36362306a36Sopenharmony_ci int ret; \ 36462306a36Sopenharmony_ci \ 36562306a36Sopenharmony_ci ret = mutex_lock_interruptible(&dt->lock); \ 36662306a36Sopenharmony_ci if (ret) \ 36762306a36Sopenharmony_ci return ret; \ 36862306a36Sopenharmony_ci __get(dt, val); \ 36962306a36Sopenharmony_ci mutex_unlock(&dt->lock); \ 37062306a36Sopenharmony_ci return 0; \ 37162306a36Sopenharmony_ci} \ 37262306a36Sopenharmony_cistatic int __fops ## _store(void *data, u64 val) \ 37362306a36Sopenharmony_ci{ \ 37462306a36Sopenharmony_ci struct tb_service *svc = data; \ 37562306a36Sopenharmony_ci struct dma_test *dt = tb_service_get_drvdata(svc); \ 37662306a36Sopenharmony_ci int ret; \ 37762306a36Sopenharmony_ci \ 37862306a36Sopenharmony_ci ret = __validate(val); \ 37962306a36Sopenharmony_ci if (ret) \ 38062306a36Sopenharmony_ci return ret; \ 38162306a36Sopenharmony_ci ret = mutex_lock_interruptible(&dt->lock); \ 38262306a36Sopenharmony_ci if (ret) \ 38362306a36Sopenharmony_ci return ret; \ 38462306a36Sopenharmony_ci __set(dt, val); \ 38562306a36Sopenharmony_ci mutex_unlock(&dt->lock); \ 38662306a36Sopenharmony_ci return 0; \ 38762306a36Sopenharmony_ci} \ 38862306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(__fops ## _fops, __fops ## _show, \ 38962306a36Sopenharmony_ci __fops ## _store, "%llu\n") 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void lanes_get(const struct dma_test *dt, u64 *val) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci *val = dt->link_width; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int lanes_validate(u64 val) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci return val > 2 ? -EINVAL : 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void lanes_set(struct dma_test *dt, u64 val) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci dt->link_width = val; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ciDMA_TEST_DEBUGFS_ATTR(lanes, lanes_get, lanes_validate, lanes_set); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void speed_get(const struct dma_test *dt, u64 *val) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci *val = dt->link_speed; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int speed_validate(u64 val) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci switch (val) { 41562306a36Sopenharmony_ci case 40: 41662306a36Sopenharmony_ci case 20: 41762306a36Sopenharmony_ci case 10: 41862306a36Sopenharmony_ci case 0: 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci default: 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void speed_set(struct dma_test *dt, u64 val) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci dt->link_speed = val; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ciDMA_TEST_DEBUGFS_ATTR(speed, speed_get, speed_validate, speed_set); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void packets_to_receive_get(const struct dma_test *dt, u64 *val) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci *val = dt->packets_to_receive; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic int packets_to_receive_validate(u64 val) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic void packets_to_receive_set(struct dma_test *dt, u64 val) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci dt->packets_to_receive = val; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ciDMA_TEST_DEBUGFS_ATTR(packets_to_receive, packets_to_receive_get, 44662306a36Sopenharmony_ci packets_to_receive_validate, packets_to_receive_set); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void packets_to_send_get(const struct dma_test *dt, u64 *val) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci *val = dt->packets_to_send; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int packets_to_send_validate(u64 val) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void packets_to_send_set(struct dma_test *dt, u64 val) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci dt->packets_to_send = val; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ciDMA_TEST_DEBUGFS_ATTR(packets_to_send, packets_to_send_get, 46362306a36Sopenharmony_ci packets_to_send_validate, packets_to_send_set); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int dma_test_set_bonding(struct dma_test *dt) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci switch (dt->link_width) { 46862306a36Sopenharmony_ci case 2: 46962306a36Sopenharmony_ci return tb_xdomain_lane_bonding_enable(dt->xd); 47062306a36Sopenharmony_ci case 1: 47162306a36Sopenharmony_ci tb_xdomain_lane_bonding_disable(dt->xd); 47262306a36Sopenharmony_ci fallthrough; 47362306a36Sopenharmony_ci default: 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic bool dma_test_validate_config(struct dma_test *dt) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci if (!dt->packets_to_send && !dt->packets_to_receive) 48162306a36Sopenharmony_ci return false; 48262306a36Sopenharmony_ci if (dt->packets_to_send && dt->packets_to_receive && 48362306a36Sopenharmony_ci dt->packets_to_send != dt->packets_to_receive) 48462306a36Sopenharmony_ci return false; 48562306a36Sopenharmony_ci return true; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void dma_test_check_errors(struct dma_test *dt, int ret) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci if (!dt->error_code) { 49162306a36Sopenharmony_ci if (dt->link_speed && dt->xd->link_speed != dt->link_speed) { 49262306a36Sopenharmony_ci dt->error_code = DMA_TEST_SPEED_ERROR; 49362306a36Sopenharmony_ci } else if (dt->link_width) { 49462306a36Sopenharmony_ci const struct tb_xdomain *xd = dt->xd; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if ((dt->link_width == 1 && xd->link_width != TB_LINK_WIDTH_SINGLE) || 49762306a36Sopenharmony_ci (dt->link_width == 2 && xd->link_width < TB_LINK_WIDTH_DUAL)) 49862306a36Sopenharmony_ci dt->error_code = DMA_TEST_WIDTH_ERROR; 49962306a36Sopenharmony_ci } else if (dt->packets_to_send != dt->packets_sent || 50062306a36Sopenharmony_ci dt->packets_to_receive != dt->packets_received || 50162306a36Sopenharmony_ci dt->crc_errors || dt->buffer_overflow_errors) { 50262306a36Sopenharmony_ci dt->error_code = DMA_TEST_PACKET_ERROR; 50362306a36Sopenharmony_ci } else { 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci dt->result = DMA_TEST_FAIL; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int test_store(void *data, u64 val) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct tb_service *svc = data; 51462306a36Sopenharmony_ci struct dma_test *dt = tb_service_get_drvdata(svc); 51562306a36Sopenharmony_ci int ret; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (val != 1) 51862306a36Sopenharmony_ci return -EINVAL; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = mutex_lock_interruptible(&dt->lock); 52162306a36Sopenharmony_ci if (ret) 52262306a36Sopenharmony_ci return ret; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci dt->packets_sent = 0; 52562306a36Sopenharmony_ci dt->packets_received = 0; 52662306a36Sopenharmony_ci dt->crc_errors = 0; 52762306a36Sopenharmony_ci dt->buffer_overflow_errors = 0; 52862306a36Sopenharmony_ci dt->result = DMA_TEST_SUCCESS; 52962306a36Sopenharmony_ci dt->error_code = DMA_TEST_NO_ERROR; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci dev_dbg(&svc->dev, "DMA test starting\n"); 53262306a36Sopenharmony_ci if (dt->link_speed) 53362306a36Sopenharmony_ci dev_dbg(&svc->dev, "link_speed: %u Gb/s\n", dt->link_speed); 53462306a36Sopenharmony_ci if (dt->link_width) 53562306a36Sopenharmony_ci dev_dbg(&svc->dev, "link_width: %u\n", dt->link_width); 53662306a36Sopenharmony_ci dev_dbg(&svc->dev, "packets_to_send: %u\n", dt->packets_to_send); 53762306a36Sopenharmony_ci dev_dbg(&svc->dev, "packets_to_receive: %u\n", dt->packets_to_receive); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (!dma_test_validate_config(dt)) { 54062306a36Sopenharmony_ci dev_err(&svc->dev, "invalid test configuration\n"); 54162306a36Sopenharmony_ci dt->error_code = DMA_TEST_CONFIG_ERROR; 54262306a36Sopenharmony_ci goto out_unlock; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ret = dma_test_set_bonding(dt); 54662306a36Sopenharmony_ci if (ret) { 54762306a36Sopenharmony_ci dev_err(&svc->dev, "failed to set lanes\n"); 54862306a36Sopenharmony_ci dt->error_code = DMA_TEST_BONDING_ERROR; 54962306a36Sopenharmony_ci goto out_unlock; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = dma_test_start_rings(dt); 55362306a36Sopenharmony_ci if (ret) { 55462306a36Sopenharmony_ci dev_err(&svc->dev, "failed to enable DMA rings\n"); 55562306a36Sopenharmony_ci dt->error_code = DMA_TEST_DMA_ERROR; 55662306a36Sopenharmony_ci goto out_unlock; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (dt->packets_to_receive) { 56062306a36Sopenharmony_ci reinit_completion(&dt->complete); 56162306a36Sopenharmony_ci ret = dma_test_submit_rx(dt, dt->packets_to_receive); 56262306a36Sopenharmony_ci if (ret) { 56362306a36Sopenharmony_ci dev_err(&svc->dev, "failed to submit receive buffers\n"); 56462306a36Sopenharmony_ci dt->error_code = DMA_TEST_BUFFER_ERROR; 56562306a36Sopenharmony_ci goto out_stop; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (dt->packets_to_send) { 57062306a36Sopenharmony_ci ret = dma_test_submit_tx(dt, dt->packets_to_send); 57162306a36Sopenharmony_ci if (ret) { 57262306a36Sopenharmony_ci dev_err(&svc->dev, "failed to submit transmit buffers\n"); 57362306a36Sopenharmony_ci dt->error_code = DMA_TEST_BUFFER_ERROR; 57462306a36Sopenharmony_ci goto out_stop; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (dt->packets_to_receive) { 57962306a36Sopenharmony_ci ret = wait_for_completion_interruptible(&dt->complete); 58062306a36Sopenharmony_ci if (ret) { 58162306a36Sopenharmony_ci dt->error_code = DMA_TEST_INTERRUPTED; 58262306a36Sopenharmony_ci goto out_stop; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciout_stop: 58762306a36Sopenharmony_ci dma_test_stop_rings(dt); 58862306a36Sopenharmony_ciout_unlock: 58962306a36Sopenharmony_ci dma_test_check_errors(dt, ret); 59062306a36Sopenharmony_ci mutex_unlock(&dt->lock); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci dev_dbg(&svc->dev, "DMA test %s\n", dma_test_result_names[dt->result]); 59362306a36Sopenharmony_ci return ret; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(test_fops, NULL, test_store, "%llu\n"); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int status_show(struct seq_file *s, void *not_used) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct tb_service *svc = s->private; 60062306a36Sopenharmony_ci struct dma_test *dt = tb_service_get_drvdata(svc); 60162306a36Sopenharmony_ci int ret; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = mutex_lock_interruptible(&dt->lock); 60462306a36Sopenharmony_ci if (ret) 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci seq_printf(s, "result: %s\n", dma_test_result_names[dt->result]); 60862306a36Sopenharmony_ci if (dt->result == DMA_TEST_NOT_RUN) 60962306a36Sopenharmony_ci goto out_unlock; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci seq_printf(s, "packets received: %u\n", dt->packets_received); 61262306a36Sopenharmony_ci seq_printf(s, "packets sent: %u\n", dt->packets_sent); 61362306a36Sopenharmony_ci seq_printf(s, "CRC errors: %u\n", dt->crc_errors); 61462306a36Sopenharmony_ci seq_printf(s, "buffer overflow errors: %u\n", 61562306a36Sopenharmony_ci dt->buffer_overflow_errors); 61662306a36Sopenharmony_ci seq_printf(s, "error: %s\n", dma_test_error_names[dt->error_code]); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciout_unlock: 61962306a36Sopenharmony_ci mutex_unlock(&dt->lock); 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(status); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void dma_test_debugfs_init(struct tb_service *svc) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct dma_test *dt = tb_service_get_drvdata(svc); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci dt->debugfs_dir = debugfs_create_dir("dma_test", svc->debugfs_dir); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci debugfs_create_file("lanes", 0600, dt->debugfs_dir, svc, &lanes_fops); 63162306a36Sopenharmony_ci debugfs_create_file("speed", 0600, dt->debugfs_dir, svc, &speed_fops); 63262306a36Sopenharmony_ci debugfs_create_file("packets_to_receive", 0600, dt->debugfs_dir, svc, 63362306a36Sopenharmony_ci &packets_to_receive_fops); 63462306a36Sopenharmony_ci debugfs_create_file("packets_to_send", 0600, dt->debugfs_dir, svc, 63562306a36Sopenharmony_ci &packets_to_send_fops); 63662306a36Sopenharmony_ci debugfs_create_file("status", 0400, dt->debugfs_dir, svc, &status_fops); 63762306a36Sopenharmony_ci debugfs_create_file("test", 0200, dt->debugfs_dir, svc, &test_fops); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct tb_xdomain *xd = tb_service_parent(svc); 64362306a36Sopenharmony_ci struct dma_test *dt; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci dt = devm_kzalloc(&svc->dev, sizeof(*dt), GFP_KERNEL); 64662306a36Sopenharmony_ci if (!dt) 64762306a36Sopenharmony_ci return -ENOMEM; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dt->svc = svc; 65062306a36Sopenharmony_ci dt->xd = xd; 65162306a36Sopenharmony_ci mutex_init(&dt->lock); 65262306a36Sopenharmony_ci init_completion(&dt->complete); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci tb_service_set_drvdata(svc, dt); 65562306a36Sopenharmony_ci dma_test_debugfs_init(svc); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic void dma_test_remove(struct tb_service *svc) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct dma_test *dt = tb_service_get_drvdata(svc); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci mutex_lock(&dt->lock); 66562306a36Sopenharmony_ci debugfs_remove_recursive(dt->debugfs_dir); 66662306a36Sopenharmony_ci mutex_unlock(&dt->lock); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int __maybe_unused dma_test_suspend(struct device *dev) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * No need to do anything special here. If userspace is writing 67362306a36Sopenharmony_ci * to the test attribute when suspend started, it comes out from 67462306a36Sopenharmony_ci * wait_for_completion_interruptible() with -ERESTARTSYS and the 67562306a36Sopenharmony_ci * DMA test fails tearing down the rings. Once userspace is 67662306a36Sopenharmony_ci * thawed the kernel restarts the write syscall effectively 67762306a36Sopenharmony_ci * re-running the test. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int __maybe_unused dma_test_resume(struct device *dev) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic const struct dev_pm_ops dma_test_pm_ops = { 68862306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(dma_test_suspend, dma_test_resume) 68962306a36Sopenharmony_ci}; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic const struct tb_service_id dma_test_ids[] = { 69262306a36Sopenharmony_ci { TB_SERVICE("dma_test", 1) }, 69362306a36Sopenharmony_ci { }, 69462306a36Sopenharmony_ci}; 69562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(tbsvc, dma_test_ids); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic struct tb_service_driver dma_test_driver = { 69862306a36Sopenharmony_ci .driver = { 69962306a36Sopenharmony_ci .owner = THIS_MODULE, 70062306a36Sopenharmony_ci .name = "thunderbolt_dma_test", 70162306a36Sopenharmony_ci .pm = &dma_test_pm_ops, 70262306a36Sopenharmony_ci }, 70362306a36Sopenharmony_ci .probe = dma_test_probe, 70462306a36Sopenharmony_ci .remove = dma_test_remove, 70562306a36Sopenharmony_ci .id_table = dma_test_ids, 70662306a36Sopenharmony_ci}; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int __init dma_test_init(void) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci u64 data_value = DMA_TEST_DATA_PATTERN; 71162306a36Sopenharmony_ci int i, ret; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci dma_test_pattern = kmalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); 71462306a36Sopenharmony_ci if (!dma_test_pattern) 71562306a36Sopenharmony_ci return -ENOMEM; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci for (i = 0; i < DMA_TEST_FRAME_SIZE / sizeof(data_value); i++) 71862306a36Sopenharmony_ci ((u32 *)dma_test_pattern)[i] = data_value++; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dma_test_dir = tb_property_create_dir(&dma_test_dir_uuid); 72162306a36Sopenharmony_ci if (!dma_test_dir) { 72262306a36Sopenharmony_ci ret = -ENOMEM; 72362306a36Sopenharmony_ci goto err_free_pattern; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci tb_property_add_immediate(dma_test_dir, "prtcid", 1); 72762306a36Sopenharmony_ci tb_property_add_immediate(dma_test_dir, "prtcvers", 1); 72862306a36Sopenharmony_ci tb_property_add_immediate(dma_test_dir, "prtcrevs", 0); 72962306a36Sopenharmony_ci tb_property_add_immediate(dma_test_dir, "prtcstns", 0); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ret = tb_register_property_dir("dma_test", dma_test_dir); 73262306a36Sopenharmony_ci if (ret) 73362306a36Sopenharmony_ci goto err_free_dir; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ret = tb_register_service_driver(&dma_test_driver); 73662306a36Sopenharmony_ci if (ret) 73762306a36Sopenharmony_ci goto err_unregister_dir; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cierr_unregister_dir: 74262306a36Sopenharmony_ci tb_unregister_property_dir("dma_test", dma_test_dir); 74362306a36Sopenharmony_cierr_free_dir: 74462306a36Sopenharmony_ci tb_property_free_dir(dma_test_dir); 74562306a36Sopenharmony_cierr_free_pattern: 74662306a36Sopenharmony_ci kfree(dma_test_pattern); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return ret; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_cimodule_init(dma_test_init); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void __exit dma_test_exit(void) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci tb_unregister_service_driver(&dma_test_driver); 75562306a36Sopenharmony_ci tb_unregister_property_dir("dma_test", dma_test_dir); 75662306a36Sopenharmony_ci tb_property_free_dir(dma_test_dir); 75762306a36Sopenharmony_ci kfree(dma_test_pattern); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_cimodule_exit(dma_test_exit); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ciMODULE_AUTHOR("Isaac Hazan <isaac.hazan@intel.com>"); 76262306a36Sopenharmony_ciMODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 76362306a36Sopenharmony_ciMODULE_DESCRIPTION("Thunderbolt/USB4 DMA traffic test driver"); 76462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 765