xref: /openbmc/linux/drivers/net/wireless/quantenna/qtnfmac/shm_ipc.c (revision 597473720f4dc69749542bfcfed4a927a43d935e)
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 
qtnf_shm_ipc_has_new_data(struct qtnf_shm_ipc * ipc)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 
qtnf_shm_handle_new_data(struct qtnf_shm_ipc * ipc)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 
qtnf_shm_ipc_irq_work(struct work_struct * work)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 
qtnf_shm_ipc_irq_inbound_handler(struct qtnf_shm_ipc * ipc)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 
qtnf_shm_ipc_irq_outbound_handler(struct qtnf_shm_ipc * ipc)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 
qtnf_shm_ipc_init(struct qtnf_shm_ipc * ipc,enum qtnf_shm_ipc_direction direction,struct qtnf_shm_ipc_region __iomem * shm_region,struct workqueue_struct * workqueue,const struct qtnf_shm_ipc_int * interrupt,const struct qtnf_shm_ipc_rx_callback * rx_callback)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 
qtnf_shm_ipc_free(struct qtnf_shm_ipc * ipc)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 
qtnf_shm_ipc_send(struct qtnf_shm_ipc * ipc,const u8 * buf,size_t size)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