1*ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+ 2*ff233cb5SSergey Matyukevich /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */ 398f44cb0SIgor Mitsyanko 498f44cb0SIgor Mitsyanko #include <linux/types.h> 598f44cb0SIgor Mitsyanko #include <linux/io.h> 698f44cb0SIgor Mitsyanko 798f44cb0SIgor Mitsyanko #include "shm_ipc.h" 898f44cb0SIgor Mitsyanko 998f44cb0SIgor Mitsyanko #undef pr_fmt 1098f44cb0SIgor Mitsyanko #define pr_fmt(fmt) "qtnfmac shm_ipc: %s: " fmt, __func__ 1198f44cb0SIgor Mitsyanko 1298f44cb0SIgor Mitsyanko static bool qtnf_shm_ipc_has_new_data(struct qtnf_shm_ipc *ipc) 1398f44cb0SIgor Mitsyanko { 1498f44cb0SIgor Mitsyanko const u32 flags = readl(&ipc->shm_region->headroom.hdr.flags); 1598f44cb0SIgor Mitsyanko 1698f44cb0SIgor Mitsyanko return (flags & QTNF_SHM_IPC_NEW_DATA); 1798f44cb0SIgor Mitsyanko } 1898f44cb0SIgor Mitsyanko 1998f44cb0SIgor Mitsyanko static void qtnf_shm_handle_new_data(struct qtnf_shm_ipc *ipc) 2098f44cb0SIgor Mitsyanko { 2198f44cb0SIgor Mitsyanko size_t size; 2298f44cb0SIgor Mitsyanko bool rx_buff_ok = true; 2398f44cb0SIgor Mitsyanko struct qtnf_shm_ipc_region_header __iomem *shm_reg_hdr; 2498f44cb0SIgor Mitsyanko 2598f44cb0SIgor Mitsyanko shm_reg_hdr = &ipc->shm_region->headroom.hdr; 2698f44cb0SIgor Mitsyanko 2798f44cb0SIgor Mitsyanko size = readw(&shm_reg_hdr->data_len); 2898f44cb0SIgor Mitsyanko 2998f44cb0SIgor Mitsyanko if (unlikely(size == 0 || size > QTN_IPC_MAX_DATA_SZ)) { 3098f44cb0SIgor Mitsyanko pr_err("wrong rx packet size: %zu\n", size); 3198f44cb0SIgor Mitsyanko rx_buff_ok = false; 328804ea9eSSergey Matyukevich } 338804ea9eSSergey Matyukevich 348804ea9eSSergey Matyukevich if (likely(rx_buff_ok)) { 358804ea9eSSergey Matyukevich ipc->rx_packet_count++; 368804ea9eSSergey Matyukevich ipc->rx_callback.fn(ipc->rx_callback.arg, 378804ea9eSSergey Matyukevich ipc->shm_region->data, size); 3898f44cb0SIgor Mitsyanko } 3998f44cb0SIgor Mitsyanko 4098f44cb0SIgor Mitsyanko writel(QTNF_SHM_IPC_ACK, &shm_reg_hdr->flags); 4198f44cb0SIgor Mitsyanko readl(&shm_reg_hdr->flags); /* flush PCIe write */ 4298f44cb0SIgor Mitsyanko 4398f44cb0SIgor Mitsyanko ipc->interrupt.fn(ipc->interrupt.arg); 4498f44cb0SIgor Mitsyanko } 4598f44cb0SIgor Mitsyanko 4698f44cb0SIgor Mitsyanko static void qtnf_shm_ipc_irq_work(struct work_struct *work) 4798f44cb0SIgor Mitsyanko { 4898f44cb0SIgor Mitsyanko struct qtnf_shm_ipc *ipc = container_of(work, struct qtnf_shm_ipc, 4998f44cb0SIgor Mitsyanko irq_work); 5098f44cb0SIgor Mitsyanko 5198f44cb0SIgor Mitsyanko while (qtnf_shm_ipc_has_new_data(ipc)) 5298f44cb0SIgor Mitsyanko qtnf_shm_handle_new_data(ipc); 5398f44cb0SIgor Mitsyanko } 5498f44cb0SIgor Mitsyanko 5598f44cb0SIgor Mitsyanko static void qtnf_shm_ipc_irq_inbound_handler(struct qtnf_shm_ipc *ipc) 5698f44cb0SIgor Mitsyanko { 5798f44cb0SIgor Mitsyanko u32 flags; 5898f44cb0SIgor Mitsyanko 5998f44cb0SIgor Mitsyanko flags = readl(&ipc->shm_region->headroom.hdr.flags); 6098f44cb0SIgor Mitsyanko 6198f44cb0SIgor Mitsyanko if (flags & QTNF_SHM_IPC_NEW_DATA) 6298f44cb0SIgor Mitsyanko queue_work(ipc->workqueue, &ipc->irq_work); 6398f44cb0SIgor Mitsyanko } 6498f44cb0SIgor Mitsyanko 6598f44cb0SIgor Mitsyanko static void qtnf_shm_ipc_irq_outbound_handler(struct qtnf_shm_ipc *ipc) 6698f44cb0SIgor Mitsyanko { 6798f44cb0SIgor Mitsyanko u32 flags; 6898f44cb0SIgor Mitsyanko 6998f44cb0SIgor Mitsyanko if (!READ_ONCE(ipc->waiting_for_ack)) 7098f44cb0SIgor Mitsyanko return; 7198f44cb0SIgor Mitsyanko 7298f44cb0SIgor Mitsyanko flags = readl(&ipc->shm_region->headroom.hdr.flags); 7398f44cb0SIgor Mitsyanko 7498f44cb0SIgor Mitsyanko if (flags & QTNF_SHM_IPC_ACK) { 7598f44cb0SIgor Mitsyanko WRITE_ONCE(ipc->waiting_for_ack, 0); 7698f44cb0SIgor Mitsyanko complete(&ipc->tx_completion); 7798f44cb0SIgor Mitsyanko } 7898f44cb0SIgor Mitsyanko } 7998f44cb0SIgor Mitsyanko 8098f44cb0SIgor Mitsyanko int qtnf_shm_ipc_init(struct qtnf_shm_ipc *ipc, 8198f44cb0SIgor Mitsyanko enum qtnf_shm_ipc_direction direction, 8298f44cb0SIgor Mitsyanko struct qtnf_shm_ipc_region __iomem *shm_region, 8398f44cb0SIgor Mitsyanko struct workqueue_struct *workqueue, 8498f44cb0SIgor Mitsyanko const struct qtnf_shm_ipc_int *interrupt, 8598f44cb0SIgor Mitsyanko const struct qtnf_shm_ipc_rx_callback *rx_callback) 8698f44cb0SIgor Mitsyanko { 8798f44cb0SIgor Mitsyanko BUILD_BUG_ON(offsetof(struct qtnf_shm_ipc_region, data) != 8898f44cb0SIgor Mitsyanko QTN_IPC_REG_HDR_SZ); 8998f44cb0SIgor Mitsyanko BUILD_BUG_ON(sizeof(struct qtnf_shm_ipc_region) > QTN_IPC_REG_SZ); 9098f44cb0SIgor Mitsyanko 9198f44cb0SIgor Mitsyanko ipc->shm_region = shm_region; 9298f44cb0SIgor Mitsyanko ipc->direction = direction; 9398f44cb0SIgor Mitsyanko ipc->interrupt = *interrupt; 9498f44cb0SIgor Mitsyanko ipc->rx_callback = *rx_callback; 9598f44cb0SIgor Mitsyanko ipc->tx_packet_count = 0; 9698f44cb0SIgor Mitsyanko ipc->rx_packet_count = 0; 9798f44cb0SIgor Mitsyanko ipc->workqueue = workqueue; 9898f44cb0SIgor Mitsyanko ipc->waiting_for_ack = 0; 9998f44cb0SIgor Mitsyanko ipc->tx_timeout_count = 0; 10098f44cb0SIgor Mitsyanko 10198f44cb0SIgor Mitsyanko switch (direction) { 10298f44cb0SIgor Mitsyanko case QTNF_SHM_IPC_OUTBOUND: 10398f44cb0SIgor Mitsyanko ipc->irq_handler = qtnf_shm_ipc_irq_outbound_handler; 10498f44cb0SIgor Mitsyanko break; 10598f44cb0SIgor Mitsyanko case QTNF_SHM_IPC_INBOUND: 10698f44cb0SIgor Mitsyanko ipc->irq_handler = qtnf_shm_ipc_irq_inbound_handler; 10798f44cb0SIgor Mitsyanko break; 10898f44cb0SIgor Mitsyanko default: 10998f44cb0SIgor Mitsyanko return -EINVAL; 11098f44cb0SIgor Mitsyanko } 11198f44cb0SIgor Mitsyanko 11298f44cb0SIgor Mitsyanko INIT_WORK(&ipc->irq_work, qtnf_shm_ipc_irq_work); 11398f44cb0SIgor Mitsyanko init_completion(&ipc->tx_completion); 11498f44cb0SIgor Mitsyanko 11598f44cb0SIgor Mitsyanko return 0; 11698f44cb0SIgor Mitsyanko } 11798f44cb0SIgor Mitsyanko 11898f44cb0SIgor Mitsyanko void qtnf_shm_ipc_free(struct qtnf_shm_ipc *ipc) 11998f44cb0SIgor Mitsyanko { 12098f44cb0SIgor Mitsyanko complete_all(&ipc->tx_completion); 12198f44cb0SIgor Mitsyanko } 12298f44cb0SIgor Mitsyanko 12398f44cb0SIgor Mitsyanko int qtnf_shm_ipc_send(struct qtnf_shm_ipc *ipc, const u8 *buf, size_t size) 12498f44cb0SIgor Mitsyanko { 12598f44cb0SIgor Mitsyanko int ret = 0; 12698f44cb0SIgor Mitsyanko struct qtnf_shm_ipc_region_header __iomem *shm_reg_hdr; 12798f44cb0SIgor Mitsyanko 12898f44cb0SIgor Mitsyanko shm_reg_hdr = &ipc->shm_region->headroom.hdr; 12998f44cb0SIgor Mitsyanko 13098f44cb0SIgor Mitsyanko if (unlikely(size > QTN_IPC_MAX_DATA_SZ)) 13198f44cb0SIgor Mitsyanko return -E2BIG; 13298f44cb0SIgor Mitsyanko 13398f44cb0SIgor Mitsyanko ipc->tx_packet_count++; 13498f44cb0SIgor Mitsyanko 13598f44cb0SIgor Mitsyanko writew(size, &shm_reg_hdr->data_len); 13698f44cb0SIgor Mitsyanko memcpy_toio(ipc->shm_region->data, buf, size); 13798f44cb0SIgor Mitsyanko 13898f44cb0SIgor Mitsyanko /* sync previous writes before proceeding */ 13998f44cb0SIgor Mitsyanko dma_wmb(); 14098f44cb0SIgor Mitsyanko 14198f44cb0SIgor Mitsyanko WRITE_ONCE(ipc->waiting_for_ack, 1); 14298f44cb0SIgor Mitsyanko 14398f44cb0SIgor Mitsyanko /* sync previous memory write before announcing new data ready */ 14498f44cb0SIgor Mitsyanko wmb(); 14598f44cb0SIgor Mitsyanko 14698f44cb0SIgor Mitsyanko writel(QTNF_SHM_IPC_NEW_DATA, &shm_reg_hdr->flags); 14798f44cb0SIgor Mitsyanko readl(&shm_reg_hdr->flags); /* flush PCIe write */ 14898f44cb0SIgor Mitsyanko 14998f44cb0SIgor Mitsyanko ipc->interrupt.fn(ipc->interrupt.arg); 15098f44cb0SIgor Mitsyanko 15198f44cb0SIgor Mitsyanko if (!wait_for_completion_timeout(&ipc->tx_completion, 15298f44cb0SIgor Mitsyanko QTN_SHM_IPC_ACK_TIMEOUT)) { 15398f44cb0SIgor Mitsyanko ret = -ETIMEDOUT; 15498f44cb0SIgor Mitsyanko ipc->tx_timeout_count++; 15598f44cb0SIgor Mitsyanko pr_err("TX ACK timeout\n"); 15698f44cb0SIgor Mitsyanko } 15798f44cb0SIgor Mitsyanko 15898f44cb0SIgor Mitsyanko /* now we're not waiting for ACK even in case of timeout */ 15998f44cb0SIgor Mitsyanko WRITE_ONCE(ipc->waiting_for_ack, 0); 16098f44cb0SIgor Mitsyanko 16198f44cb0SIgor Mitsyanko return ret; 16298f44cb0SIgor Mitsyanko } 163