162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2020, MIPI Alliance, Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Nicolas Pitre <npitre@baylibre.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/i3c/master.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "hci.h"
1562306a36Sopenharmony_ci#include "cmd.h"
1662306a36Sopenharmony_ci#include "ibi.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * PIO Access Area
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define pio_reg_read(r)		readl(hci->PIO_regs + (PIO_##r))
2462306a36Sopenharmony_ci#define pio_reg_write(r, v)	writel(v, hci->PIO_regs + (PIO_##r))
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define PIO_COMMAND_QUEUE_PORT		0x00
2762306a36Sopenharmony_ci#define PIO_RESPONSE_QUEUE_PORT		0x04
2862306a36Sopenharmony_ci#define PIO_XFER_DATA_PORT		0x08
2962306a36Sopenharmony_ci#define PIO_IBI_PORT			0x0c
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define PIO_QUEUE_THLD_CTRL		0x10
3262306a36Sopenharmony_ci#define QUEUE_IBI_STATUS_THLD		GENMASK(31, 24)
3362306a36Sopenharmony_ci#define QUEUE_IBI_DATA_THLD		GENMASK(23, 16)
3462306a36Sopenharmony_ci#define QUEUE_RESP_BUF_THLD		GENMASK(15, 8)
3562306a36Sopenharmony_ci#define QUEUE_CMD_EMPTY_BUF_THLD	GENMASK(7, 0)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define PIO_DATA_BUFFER_THLD_CTRL	0x14
3862306a36Sopenharmony_ci#define DATA_RX_START_THLD		GENMASK(26, 24)
3962306a36Sopenharmony_ci#define DATA_TX_START_THLD		GENMASK(18, 16)
4062306a36Sopenharmony_ci#define DATA_RX_BUF_THLD		GENMASK(10, 8)
4162306a36Sopenharmony_ci#define DATA_TX_BUF_THLD		GENMASK(2, 0)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define PIO_QUEUE_SIZE			0x18
4462306a36Sopenharmony_ci#define TX_DATA_BUFFER_SIZE		GENMASK(31, 24)
4562306a36Sopenharmony_ci#define RX_DATA_BUFFER_SIZE		GENMASK(23, 16)
4662306a36Sopenharmony_ci#define IBI_STATUS_SIZE			GENMASK(15, 8)
4762306a36Sopenharmony_ci#define CR_QUEUE_SIZE			GENMASK(7, 0)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define PIO_INTR_STATUS			0x20
5062306a36Sopenharmony_ci#define PIO_INTR_STATUS_ENABLE		0x24
5162306a36Sopenharmony_ci#define PIO_INTR_SIGNAL_ENABLE		0x28
5262306a36Sopenharmony_ci#define PIO_INTR_FORCE			0x2c
5362306a36Sopenharmony_ci#define STAT_TRANSFER_BLOCKED		BIT(25)
5462306a36Sopenharmony_ci#define STAT_PERR_RESP_UFLOW		BIT(24)
5562306a36Sopenharmony_ci#define STAT_PERR_CMD_OFLOW		BIT(23)
5662306a36Sopenharmony_ci#define STAT_PERR_IBI_UFLOW		BIT(22)
5762306a36Sopenharmony_ci#define STAT_PERR_RX_UFLOW		BIT(21)
5862306a36Sopenharmony_ci#define STAT_PERR_TX_OFLOW		BIT(20)
5962306a36Sopenharmony_ci#define STAT_ERR_RESP_QUEUE_FULL	BIT(19)
6062306a36Sopenharmony_ci#define STAT_WARN_RESP_QUEUE_FULL	BIT(18)
6162306a36Sopenharmony_ci#define STAT_ERR_IBI_QUEUE_FULL		BIT(17)
6262306a36Sopenharmony_ci#define STAT_WARN_IBI_QUEUE_FULL	BIT(16)
6362306a36Sopenharmony_ci#define STAT_ERR_RX_DATA_FULL		BIT(15)
6462306a36Sopenharmony_ci#define STAT_WARN_RX_DATA_FULL		BIT(14)
6562306a36Sopenharmony_ci#define STAT_ERR_TX_DATA_EMPTY		BIT(13)
6662306a36Sopenharmony_ci#define STAT_WARN_TX_DATA_EMPTY		BIT(12)
6762306a36Sopenharmony_ci#define STAT_TRANSFER_ERR		BIT(9)
6862306a36Sopenharmony_ci#define STAT_WARN_INS_STOP_MODE		BIT(7)
6962306a36Sopenharmony_ci#define STAT_TRANSFER_ABORT		BIT(5)
7062306a36Sopenharmony_ci#define STAT_RESP_READY			BIT(4)
7162306a36Sopenharmony_ci#define STAT_CMD_QUEUE_READY		BIT(3)
7262306a36Sopenharmony_ci#define STAT_IBI_STATUS_THLD		BIT(2)
7362306a36Sopenharmony_ci#define STAT_RX_THLD			BIT(1)
7462306a36Sopenharmony_ci#define STAT_TX_THLD			BIT(0)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define PIO_QUEUE_CUR_STATUS		0x38
7762306a36Sopenharmony_ci#define CUR_IBI_Q_LEVEL			GENMASK(28, 20)
7862306a36Sopenharmony_ci#define CUR_RESP_Q_LEVEL		GENMASK(18, 10)
7962306a36Sopenharmony_ci#define CUR_CMD_Q_EMPTY_LEVEL		GENMASK(8, 0)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define PIO_DATA_BUFFER_CUR_STATUS	0x3c
8262306a36Sopenharmony_ci#define CUR_RX_BUF_LVL			GENMASK(26, 16)
8362306a36Sopenharmony_ci#define CUR_TX_BUF_LVL			GENMASK(10, 0)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * Handy status bit combinations
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define STAT_LATENCY_WARNINGS		(STAT_WARN_RESP_QUEUE_FULL | \
9062306a36Sopenharmony_ci					 STAT_WARN_IBI_QUEUE_FULL | \
9162306a36Sopenharmony_ci					 STAT_WARN_RX_DATA_FULL | \
9262306a36Sopenharmony_ci					 STAT_WARN_TX_DATA_EMPTY | \
9362306a36Sopenharmony_ci					 STAT_WARN_INS_STOP_MODE)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define STAT_LATENCY_ERRORS		(STAT_ERR_RESP_QUEUE_FULL | \
9662306a36Sopenharmony_ci					 STAT_ERR_IBI_QUEUE_FULL | \
9762306a36Sopenharmony_ci					 STAT_ERR_RX_DATA_FULL | \
9862306a36Sopenharmony_ci					 STAT_ERR_TX_DATA_EMPTY)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define STAT_PROG_ERRORS		(STAT_TRANSFER_BLOCKED | \
10162306a36Sopenharmony_ci					 STAT_PERR_RESP_UFLOW | \
10262306a36Sopenharmony_ci					 STAT_PERR_CMD_OFLOW | \
10362306a36Sopenharmony_ci					 STAT_PERR_IBI_UFLOW | \
10462306a36Sopenharmony_ci					 STAT_PERR_RX_UFLOW | \
10562306a36Sopenharmony_ci					 STAT_PERR_TX_OFLOW)
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define STAT_ALL_ERRORS			(STAT_TRANSFER_ABORT | \
10862306a36Sopenharmony_ci					 STAT_TRANSFER_ERR | \
10962306a36Sopenharmony_ci					 STAT_LATENCY_ERRORS | \
11062306a36Sopenharmony_ci					 STAT_PROG_ERRORS)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistruct hci_pio_dev_ibi_data {
11362306a36Sopenharmony_ci	struct i3c_generic_ibi_pool *pool;
11462306a36Sopenharmony_ci	unsigned int max_len;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct hci_pio_ibi_data {
11862306a36Sopenharmony_ci	struct i3c_ibi_slot *slot;
11962306a36Sopenharmony_ci	void *data_ptr;
12062306a36Sopenharmony_ci	unsigned int addr;
12162306a36Sopenharmony_ci	unsigned int seg_len, seg_cnt;
12262306a36Sopenharmony_ci	unsigned int max_len;
12362306a36Sopenharmony_ci	bool last_seg;
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct hci_pio_data {
12762306a36Sopenharmony_ci	spinlock_t lock;
12862306a36Sopenharmony_ci	struct hci_xfer *curr_xfer, *xfer_queue;
12962306a36Sopenharmony_ci	struct hci_xfer *curr_rx, *rx_queue;
13062306a36Sopenharmony_ci	struct hci_xfer *curr_tx, *tx_queue;
13162306a36Sopenharmony_ci	struct hci_xfer *curr_resp, *resp_queue;
13262306a36Sopenharmony_ci	struct hci_pio_ibi_data ibi;
13362306a36Sopenharmony_ci	unsigned int rx_thresh_size, tx_thresh_size;
13462306a36Sopenharmony_ci	unsigned int max_ibi_thresh;
13562306a36Sopenharmony_ci	u32 reg_queue_thresh;
13662306a36Sopenharmony_ci	u32 enabled_irqs;
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int hci_pio_init(struct i3c_hci *hci)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct hci_pio_data *pio;
14262306a36Sopenharmony_ci	u32 val, size_val, rx_thresh, tx_thresh, ibi_val;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	pio = kzalloc(sizeof(*pio), GFP_KERNEL);
14562306a36Sopenharmony_ci	if (!pio)
14662306a36Sopenharmony_ci		return -ENOMEM;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	hci->io_data = pio;
14962306a36Sopenharmony_ci	spin_lock_init(&pio->lock);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	size_val = pio_reg_read(QUEUE_SIZE);
15262306a36Sopenharmony_ci	dev_info(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
15362306a36Sopenharmony_ci		 FIELD_GET(CR_QUEUE_SIZE, size_val));
15462306a36Sopenharmony_ci	dev_info(&hci->master.dev, "IBI FIFO = %ld bytes\n",
15562306a36Sopenharmony_ci		 4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
15662306a36Sopenharmony_ci	dev_info(&hci->master.dev, "RX data FIFO = %d bytes\n",
15762306a36Sopenharmony_ci		 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
15862306a36Sopenharmony_ci	dev_info(&hci->master.dev, "TX data FIFO = %d bytes\n",
15962306a36Sopenharmony_ci		 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/*
16262306a36Sopenharmony_ci	 * Let's initialize data thresholds to half of the actual FIFO size.
16362306a36Sopenharmony_ci	 * The start thresholds aren't used (set to 0) as the FIFO is always
16462306a36Sopenharmony_ci	 * serviced before the corresponding command is queued.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	rx_thresh = FIELD_GET(RX_DATA_BUFFER_SIZE, size_val);
16762306a36Sopenharmony_ci	tx_thresh = FIELD_GET(TX_DATA_BUFFER_SIZE, size_val);
16862306a36Sopenharmony_ci	if (hci->version_major == 1) {
16962306a36Sopenharmony_ci		/* those are expressed as 2^[n+1), so just sub 1 if not 0 */
17062306a36Sopenharmony_ci		if (rx_thresh)
17162306a36Sopenharmony_ci			rx_thresh -= 1;
17262306a36Sopenharmony_ci		if (tx_thresh)
17362306a36Sopenharmony_ci			tx_thresh -= 1;
17462306a36Sopenharmony_ci		pio->rx_thresh_size = 2 << rx_thresh;
17562306a36Sopenharmony_ci		pio->tx_thresh_size = 2 << tx_thresh;
17662306a36Sopenharmony_ci	} else {
17762306a36Sopenharmony_ci		/* size is 2^(n+1) and threshold is 2^n i.e. already halved */
17862306a36Sopenharmony_ci		pio->rx_thresh_size = 1 << rx_thresh;
17962306a36Sopenharmony_ci		pio->tx_thresh_size = 1 << tx_thresh;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci	val = FIELD_PREP(DATA_RX_BUF_THLD,   rx_thresh) |
18262306a36Sopenharmony_ci	      FIELD_PREP(DATA_TX_BUF_THLD,   tx_thresh);
18362306a36Sopenharmony_ci	pio_reg_write(DATA_BUFFER_THLD_CTRL, val);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Let's raise an interrupt as soon as there is one free cmd slot
18762306a36Sopenharmony_ci	 * or one available response or IBI. For IBI data let's use half the
18862306a36Sopenharmony_ci	 * IBI queue size within allowed bounds.
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	ibi_val = FIELD_GET(IBI_STATUS_SIZE, size_val);
19162306a36Sopenharmony_ci	pio->max_ibi_thresh = clamp_val(ibi_val/2, 1, 63);
19262306a36Sopenharmony_ci	val = FIELD_PREP(QUEUE_IBI_STATUS_THLD, 1) |
19362306a36Sopenharmony_ci	      FIELD_PREP(QUEUE_IBI_DATA_THLD, pio->max_ibi_thresh) |
19462306a36Sopenharmony_ci	      FIELD_PREP(QUEUE_RESP_BUF_THLD, 1) |
19562306a36Sopenharmony_ci	      FIELD_PREP(QUEUE_CMD_EMPTY_BUF_THLD, 1);
19662306a36Sopenharmony_ci	pio_reg_write(QUEUE_THLD_CTRL, val);
19762306a36Sopenharmony_ci	pio->reg_queue_thresh = val;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Disable all IRQs but allow all status bits */
20062306a36Sopenharmony_ci	pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
20162306a36Sopenharmony_ci	pio_reg_write(INTR_STATUS_ENABLE, 0xffffffff);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Always accept error interrupts (will be activated on first xfer) */
20462306a36Sopenharmony_ci	pio->enabled_irqs = STAT_ALL_ERRORS;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void hci_pio_cleanup(struct i3c_hci *hci)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct hci_pio_data *pio = hci->io_data;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (pio) {
21662306a36Sopenharmony_ci		DBG("status = %#x/%#x",
21762306a36Sopenharmony_ci		    pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
21862306a36Sopenharmony_ci		BUG_ON(pio->curr_xfer);
21962306a36Sopenharmony_ci		BUG_ON(pio->curr_rx);
22062306a36Sopenharmony_ci		BUG_ON(pio->curr_tx);
22162306a36Sopenharmony_ci		BUG_ON(pio->curr_resp);
22262306a36Sopenharmony_ci		kfree(pio);
22362306a36Sopenharmony_ci		hci->io_data = NULL;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void hci_pio_write_cmd(struct i3c_hci *hci, struct hci_xfer *xfer)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	DBG("cmd_desc[%d] = 0x%08x", 0, xfer->cmd_desc[0]);
23062306a36Sopenharmony_ci	DBG("cmd_desc[%d] = 0x%08x", 1, xfer->cmd_desc[1]);
23162306a36Sopenharmony_ci	pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[0]);
23262306a36Sopenharmony_ci	pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[1]);
23362306a36Sopenharmony_ci	if (hci->cmd == &mipi_i3c_hci_cmd_v2) {
23462306a36Sopenharmony_ci		DBG("cmd_desc[%d] = 0x%08x", 2, xfer->cmd_desc[2]);
23562306a36Sopenharmony_ci		DBG("cmd_desc[%d] = 0x%08x", 3, xfer->cmd_desc[3]);
23662306a36Sopenharmony_ci		pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[2]);
23762306a36Sopenharmony_ci		pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[3]);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic bool hci_pio_do_rx(struct i3c_hci *hci, struct hci_pio_data *pio)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct hci_xfer *xfer = pio->curr_rx;
24462306a36Sopenharmony_ci	unsigned int nr_words;
24562306a36Sopenharmony_ci	u32 *p;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	p = xfer->data;
24862306a36Sopenharmony_ci	p += (xfer->data_len - xfer->data_left) / 4;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	while (xfer->data_left >= 4) {
25162306a36Sopenharmony_ci		/* bail out if FIFO hasn't reached the threshold value yet */
25262306a36Sopenharmony_ci		if (!(pio_reg_read(INTR_STATUS) & STAT_RX_THLD))
25362306a36Sopenharmony_ci			return false;
25462306a36Sopenharmony_ci		nr_words = min(xfer->data_left / 4, pio->rx_thresh_size);
25562306a36Sopenharmony_ci		/* extract data from FIFO */
25662306a36Sopenharmony_ci		xfer->data_left -= nr_words * 4;
25762306a36Sopenharmony_ci		DBG("now %d left %d", nr_words * 4, xfer->data_left);
25862306a36Sopenharmony_ci		while (nr_words--)
25962306a36Sopenharmony_ci			*p++ = pio_reg_read(XFER_DATA_PORT);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* trailing data is retrieved upon response reception */
26362306a36Sopenharmony_ci	return !xfer->data_left;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void hci_pio_do_trailing_rx(struct i3c_hci *hci,
26762306a36Sopenharmony_ci				   struct hci_pio_data *pio, unsigned int count)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct hci_xfer *xfer = pio->curr_rx;
27062306a36Sopenharmony_ci	u32 *p;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	DBG("%d remaining", count);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	p = xfer->data;
27562306a36Sopenharmony_ci	p += (xfer->data_len - xfer->data_left) / 4;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (count >= 4) {
27862306a36Sopenharmony_ci		unsigned int nr_words = count / 4;
27962306a36Sopenharmony_ci		/* extract data from FIFO */
28062306a36Sopenharmony_ci		xfer->data_left -= nr_words * 4;
28162306a36Sopenharmony_ci		DBG("now %d left %d", nr_words * 4, xfer->data_left);
28262306a36Sopenharmony_ci		while (nr_words--)
28362306a36Sopenharmony_ci			*p++ = pio_reg_read(XFER_DATA_PORT);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	count &= 3;
28762306a36Sopenharmony_ci	if (count) {
28862306a36Sopenharmony_ci		/*
28962306a36Sopenharmony_ci		 * There are trailing bytes in the last word.
29062306a36Sopenharmony_ci		 * Fetch it and extract bytes in an endian independent way.
29162306a36Sopenharmony_ci		 * Unlike the TX case, we must not write memory past the
29262306a36Sopenharmony_ci		 * end of the destination buffer.
29362306a36Sopenharmony_ci		 */
29462306a36Sopenharmony_ci		u8 *p_byte = (u8 *)p;
29562306a36Sopenharmony_ci		u32 data = pio_reg_read(XFER_DATA_PORT);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		xfer->data_word_before_partial = data;
29862306a36Sopenharmony_ci		xfer->data_left -= count;
29962306a36Sopenharmony_ci		data = (__force u32) cpu_to_le32(data);
30062306a36Sopenharmony_ci		while (count--) {
30162306a36Sopenharmony_ci			*p_byte++ = data;
30262306a36Sopenharmony_ci			data >>= 8;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct hci_xfer *xfer = pio->curr_tx;
31062306a36Sopenharmony_ci	unsigned int nr_words;
31162306a36Sopenharmony_ci	u32 *p;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	p = xfer->data;
31462306a36Sopenharmony_ci	p += (xfer->data_len - xfer->data_left) / 4;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	while (xfer->data_left >= 4) {
31762306a36Sopenharmony_ci		/* bail out if FIFO free space is below set threshold */
31862306a36Sopenharmony_ci		if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD))
31962306a36Sopenharmony_ci			return false;
32062306a36Sopenharmony_ci		/* we can fill up to that TX threshold */
32162306a36Sopenharmony_ci		nr_words = min(xfer->data_left / 4, pio->tx_thresh_size);
32262306a36Sopenharmony_ci		/* push data into the FIFO */
32362306a36Sopenharmony_ci		xfer->data_left -= nr_words * 4;
32462306a36Sopenharmony_ci		DBG("now %d left %d", nr_words * 4, xfer->data_left);
32562306a36Sopenharmony_ci		while (nr_words--)
32662306a36Sopenharmony_ci			pio_reg_write(XFER_DATA_PORT, *p++);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (xfer->data_left) {
33062306a36Sopenharmony_ci		/*
33162306a36Sopenharmony_ci		 * There are trailing bytes to send. We can simply load
33262306a36Sopenharmony_ci		 * them from memory as a word which will keep those bytes
33362306a36Sopenharmony_ci		 * in their proper place even on a BE system. This will
33462306a36Sopenharmony_ci		 * also get some bytes past the actual buffer but no one
33562306a36Sopenharmony_ci		 * should care as they won't be sent out.
33662306a36Sopenharmony_ci		 */
33762306a36Sopenharmony_ci		if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD))
33862306a36Sopenharmony_ci			return false;
33962306a36Sopenharmony_ci		DBG("trailing %d", xfer->data_left);
34062306a36Sopenharmony_ci		pio_reg_write(XFER_DATA_PORT, *p);
34162306a36Sopenharmony_ci		xfer->data_left = 0;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return true;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic bool hci_pio_process_rx(struct i3c_hci *hci, struct hci_pio_data *pio)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	while (pio->curr_rx && hci_pio_do_rx(hci, pio))
35062306a36Sopenharmony_ci		pio->curr_rx = pio->curr_rx->next_data;
35162306a36Sopenharmony_ci	return !pio->curr_rx;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic bool hci_pio_process_tx(struct i3c_hci *hci, struct hci_pio_data *pio)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	while (pio->curr_tx && hci_pio_do_tx(hci, pio))
35762306a36Sopenharmony_ci		pio->curr_tx = pio->curr_tx->next_data;
35862306a36Sopenharmony_ci	return !pio->curr_tx;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic void hci_pio_queue_data(struct i3c_hci *hci, struct hci_pio_data *pio)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct hci_xfer *xfer = pio->curr_xfer;
36462306a36Sopenharmony_ci	struct hci_xfer *prev_queue_tail;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!xfer->data) {
36762306a36Sopenharmony_ci		xfer->data_len = xfer->data_left = 0;
36862306a36Sopenharmony_ci		return;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (xfer->rnw) {
37262306a36Sopenharmony_ci		prev_queue_tail = pio->rx_queue;
37362306a36Sopenharmony_ci		pio->rx_queue = xfer;
37462306a36Sopenharmony_ci		if (pio->curr_rx) {
37562306a36Sopenharmony_ci			prev_queue_tail->next_data = xfer;
37662306a36Sopenharmony_ci		} else {
37762306a36Sopenharmony_ci			pio->curr_rx = xfer;
37862306a36Sopenharmony_ci			if (!hci_pio_process_rx(hci, pio))
37962306a36Sopenharmony_ci				pio->enabled_irqs |= STAT_RX_THLD;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	} else {
38262306a36Sopenharmony_ci		prev_queue_tail = pio->tx_queue;
38362306a36Sopenharmony_ci		pio->tx_queue = xfer;
38462306a36Sopenharmony_ci		if (pio->curr_tx) {
38562306a36Sopenharmony_ci			prev_queue_tail->next_data = xfer;
38662306a36Sopenharmony_ci		} else {
38762306a36Sopenharmony_ci			pio->curr_tx = xfer;
38862306a36Sopenharmony_ci			if (!hci_pio_process_tx(hci, pio))
38962306a36Sopenharmony_ci				pio->enabled_irqs |= STAT_TX_THLD;
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void hci_pio_push_to_next_rx(struct i3c_hci *hci, struct hci_xfer *xfer,
39562306a36Sopenharmony_ci				    unsigned int words_to_keep)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	u32 *from = xfer->data;
39862306a36Sopenharmony_ci	u32 from_last;
39962306a36Sopenharmony_ci	unsigned int received, count;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	received = (xfer->data_len - xfer->data_left) / 4;
40262306a36Sopenharmony_ci	if ((xfer->data_len - xfer->data_left) & 3) {
40362306a36Sopenharmony_ci		from_last = xfer->data_word_before_partial;
40462306a36Sopenharmony_ci		received += 1;
40562306a36Sopenharmony_ci	} else {
40662306a36Sopenharmony_ci		from_last = from[received];
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	from += words_to_keep;
40962306a36Sopenharmony_ci	count = received - words_to_keep;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	while (count) {
41262306a36Sopenharmony_ci		unsigned int room, left, chunk, bytes_to_move;
41362306a36Sopenharmony_ci		u32 last_word;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		xfer = xfer->next_data;
41662306a36Sopenharmony_ci		if (!xfer) {
41762306a36Sopenharmony_ci			dev_err(&hci->master.dev, "pushing RX data to unexistent xfer\n");
41862306a36Sopenharmony_ci			return;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		room = DIV_ROUND_UP(xfer->data_len, 4);
42262306a36Sopenharmony_ci		left = DIV_ROUND_UP(xfer->data_left, 4);
42362306a36Sopenharmony_ci		chunk = min(count, room);
42462306a36Sopenharmony_ci		if (chunk > left) {
42562306a36Sopenharmony_ci			hci_pio_push_to_next_rx(hci, xfer, chunk - left);
42662306a36Sopenharmony_ci			left = chunk;
42762306a36Sopenharmony_ci			xfer->data_left = left * 4;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		bytes_to_move = xfer->data_len - xfer->data_left;
43162306a36Sopenharmony_ci		if (bytes_to_move & 3) {
43262306a36Sopenharmony_ci			/* preserve word  to become partial */
43362306a36Sopenharmony_ci			u32 *p = xfer->data;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci			xfer->data_word_before_partial = p[bytes_to_move / 4];
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci		memmove(xfer->data + chunk, xfer->data, bytes_to_move);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		/* treat last word specially because of partial word issues */
44062306a36Sopenharmony_ci		chunk -= 1;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		memcpy(xfer->data, from, chunk * 4);
44362306a36Sopenharmony_ci		xfer->data_left -= chunk * 4;
44462306a36Sopenharmony_ci		from += chunk;
44562306a36Sopenharmony_ci		count -= chunk;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		last_word = (count == 1) ? from_last : *from++;
44862306a36Sopenharmony_ci		if (xfer->data_left < 4) {
44962306a36Sopenharmony_ci			/*
45062306a36Sopenharmony_ci			 * Like in hci_pio_do_trailing_rx(), preserve original
45162306a36Sopenharmony_ci			 * word to be stored partially then store bytes it
45262306a36Sopenharmony_ci			 * in an endian independent way.
45362306a36Sopenharmony_ci			 */
45462306a36Sopenharmony_ci			u8 *p_byte = xfer->data;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci			p_byte += chunk * 4;
45762306a36Sopenharmony_ci			xfer->data_word_before_partial = last_word;
45862306a36Sopenharmony_ci			last_word = (__force u32) cpu_to_le32(last_word);
45962306a36Sopenharmony_ci			while (xfer->data_left--) {
46062306a36Sopenharmony_ci				*p_byte++ = last_word;
46162306a36Sopenharmony_ci				last_word >>= 8;
46262306a36Sopenharmony_ci			}
46362306a36Sopenharmony_ci		} else {
46462306a36Sopenharmony_ci			u32 *p = xfer->data;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci			p[chunk] = last_word;
46762306a36Sopenharmony_ci			xfer->data_left -= 4;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci		count--;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic void hci_pio_err(struct i3c_hci *hci, struct hci_pio_data *pio,
47462306a36Sopenharmony_ci			u32 status);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	while (pio->curr_resp &&
47962306a36Sopenharmony_ci	       (pio_reg_read(INTR_STATUS) & STAT_RESP_READY)) {
48062306a36Sopenharmony_ci		struct hci_xfer *xfer = pio->curr_resp;
48162306a36Sopenharmony_ci		u32 resp = pio_reg_read(RESPONSE_QUEUE_PORT);
48262306a36Sopenharmony_ci		unsigned int tid = RESP_TID(resp);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		DBG("resp = 0x%08x", resp);
48562306a36Sopenharmony_ci		if (tid != xfer->cmd_tid) {
48662306a36Sopenharmony_ci			dev_err(&hci->master.dev,
48762306a36Sopenharmony_ci				"response tid=%d when expecting %d\n",
48862306a36Sopenharmony_ci				tid, xfer->cmd_tid);
48962306a36Sopenharmony_ci			/* let's pretend it is a prog error... any of them  */
49062306a36Sopenharmony_ci			hci_pio_err(hci, pio, STAT_PROG_ERRORS);
49162306a36Sopenharmony_ci			return false;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		xfer->response = resp;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		if (pio->curr_rx == xfer) {
49662306a36Sopenharmony_ci			/*
49762306a36Sopenharmony_ci			 * Response availability implies RX completion.
49862306a36Sopenharmony_ci			 * Retrieve trailing RX data if any.
49962306a36Sopenharmony_ci			 * Note that short reads are possible.
50062306a36Sopenharmony_ci			 */
50162306a36Sopenharmony_ci			unsigned int received, expected, to_keep;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			received = xfer->data_len - xfer->data_left;
50462306a36Sopenharmony_ci			expected = RESP_DATA_LENGTH(xfer->response);
50562306a36Sopenharmony_ci			if (expected > received) {
50662306a36Sopenharmony_ci				hci_pio_do_trailing_rx(hci, pio,
50762306a36Sopenharmony_ci						       expected - received);
50862306a36Sopenharmony_ci			} else if (received > expected) {
50962306a36Sopenharmony_ci				/* we consumed data meant for next xfer */
51062306a36Sopenharmony_ci				to_keep = DIV_ROUND_UP(expected, 4);
51162306a36Sopenharmony_ci				hci_pio_push_to_next_rx(hci, xfer, to_keep);
51262306a36Sopenharmony_ci			}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci			/* then process the RX list pointer */
51562306a36Sopenharmony_ci			if (hci_pio_process_rx(hci, pio))
51662306a36Sopenharmony_ci				pio->enabled_irqs &= ~STAT_RX_THLD;
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		/*
52062306a36Sopenharmony_ci		 * We're about to give back ownership of the xfer structure
52162306a36Sopenharmony_ci		 * to the waiting instance. Make sure no reference to it
52262306a36Sopenharmony_ci		 * still exists.
52362306a36Sopenharmony_ci		 */
52462306a36Sopenharmony_ci		if (pio->curr_rx == xfer) {
52562306a36Sopenharmony_ci			DBG("short RX ?");
52662306a36Sopenharmony_ci			pio->curr_rx = pio->curr_rx->next_data;
52762306a36Sopenharmony_ci		} else if (pio->curr_tx == xfer) {
52862306a36Sopenharmony_ci			DBG("short TX ?");
52962306a36Sopenharmony_ci			pio->curr_tx = pio->curr_tx->next_data;
53062306a36Sopenharmony_ci		} else if (xfer->data_left) {
53162306a36Sopenharmony_ci			DBG("PIO xfer count = %d after response",
53262306a36Sopenharmony_ci			    xfer->data_left);
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		pio->curr_resp = xfer->next_resp;
53662306a36Sopenharmony_ci		if (xfer->completion)
53762306a36Sopenharmony_ci			complete(xfer->completion);
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	return !pio->curr_resp;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic void hci_pio_queue_resp(struct i3c_hci *hci, struct hci_pio_data *pio)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct hci_xfer *xfer = pio->curr_xfer;
54562306a36Sopenharmony_ci	struct hci_xfer *prev_queue_tail;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (!(xfer->cmd_desc[0] & CMD_0_ROC))
54862306a36Sopenharmony_ci		return;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	prev_queue_tail = pio->resp_queue;
55162306a36Sopenharmony_ci	pio->resp_queue = xfer;
55262306a36Sopenharmony_ci	if (pio->curr_resp) {
55362306a36Sopenharmony_ci		prev_queue_tail->next_resp = xfer;
55462306a36Sopenharmony_ci	} else {
55562306a36Sopenharmony_ci		pio->curr_resp = xfer;
55662306a36Sopenharmony_ci		if (!hci_pio_process_resp(hci, pio))
55762306a36Sopenharmony_ci			pio->enabled_irqs |= STAT_RESP_READY;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic bool hci_pio_process_cmd(struct i3c_hci *hci, struct hci_pio_data *pio)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	while (pio->curr_xfer &&
56462306a36Sopenharmony_ci	       (pio_reg_read(INTR_STATUS) & STAT_CMD_QUEUE_READY)) {
56562306a36Sopenharmony_ci		/*
56662306a36Sopenharmony_ci		 * Always process the data FIFO before sending the command
56762306a36Sopenharmony_ci		 * so needed TX data or RX space is available upfront.
56862306a36Sopenharmony_ci		 */
56962306a36Sopenharmony_ci		hci_pio_queue_data(hci, pio);
57062306a36Sopenharmony_ci		/*
57162306a36Sopenharmony_ci		 * Then queue our response request. This will also process
57262306a36Sopenharmony_ci		 * the response FIFO in case it got suddenly filled up
57362306a36Sopenharmony_ci		 * with results from previous commands.
57462306a36Sopenharmony_ci		 */
57562306a36Sopenharmony_ci		hci_pio_queue_resp(hci, pio);
57662306a36Sopenharmony_ci		/*
57762306a36Sopenharmony_ci		 * Finally send the command.
57862306a36Sopenharmony_ci		 */
57962306a36Sopenharmony_ci		hci_pio_write_cmd(hci, pio->curr_xfer);
58062306a36Sopenharmony_ci		/*
58162306a36Sopenharmony_ci		 * And move on.
58262306a36Sopenharmony_ci		 */
58362306a36Sopenharmony_ci		pio->curr_xfer = pio->curr_xfer->next_xfer;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci	return !pio->curr_xfer;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct hci_pio_data *pio = hci->io_data;
59162306a36Sopenharmony_ci	struct hci_xfer *prev_queue_tail;
59262306a36Sopenharmony_ci	int i;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	DBG("n = %d", n);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/* link xfer instances together and initialize data count */
59762306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
59862306a36Sopenharmony_ci		xfer[i].next_xfer = (i + 1 < n) ? &xfer[i + 1] : NULL;
59962306a36Sopenharmony_ci		xfer[i].next_data = NULL;
60062306a36Sopenharmony_ci		xfer[i].next_resp = NULL;
60162306a36Sopenharmony_ci		xfer[i].data_left = xfer[i].data_len;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	spin_lock_irq(&pio->lock);
60562306a36Sopenharmony_ci	prev_queue_tail = pio->xfer_queue;
60662306a36Sopenharmony_ci	pio->xfer_queue = &xfer[n - 1];
60762306a36Sopenharmony_ci	if (pio->curr_xfer) {
60862306a36Sopenharmony_ci		prev_queue_tail->next_xfer = xfer;
60962306a36Sopenharmony_ci	} else {
61062306a36Sopenharmony_ci		pio->curr_xfer = xfer;
61162306a36Sopenharmony_ci		if (!hci_pio_process_cmd(hci, pio))
61262306a36Sopenharmony_ci			pio->enabled_irqs |= STAT_CMD_QUEUE_READY;
61362306a36Sopenharmony_ci		pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
61462306a36Sopenharmony_ci		DBG("status = %#x/%#x",
61562306a36Sopenharmony_ci		    pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	spin_unlock_irq(&pio->lock);
61862306a36Sopenharmony_ci	return 0;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic bool hci_pio_dequeue_xfer_common(struct i3c_hci *hci,
62262306a36Sopenharmony_ci					struct hci_pio_data *pio,
62362306a36Sopenharmony_ci					struct hci_xfer *xfer, int n)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct hci_xfer *p, **p_prev_next;
62662306a36Sopenharmony_ci	int i;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/*
62962306a36Sopenharmony_ci	 * To safely dequeue a transfer request, it must be either entirely
63062306a36Sopenharmony_ci	 * processed, or not yet processed at all. If our request tail is
63162306a36Sopenharmony_ci	 * reachable from either the data or resp list that means the command
63262306a36Sopenharmony_ci	 * was submitted and not yet completed.
63362306a36Sopenharmony_ci	 */
63462306a36Sopenharmony_ci	for (p = pio->curr_resp; p; p = p->next_resp)
63562306a36Sopenharmony_ci		for (i = 0; i < n; i++)
63662306a36Sopenharmony_ci			if (p == &xfer[i])
63762306a36Sopenharmony_ci				goto pio_screwed;
63862306a36Sopenharmony_ci	for (p = pio->curr_rx; p; p = p->next_data)
63962306a36Sopenharmony_ci		for (i = 0; i < n; i++)
64062306a36Sopenharmony_ci			if (p == &xfer[i])
64162306a36Sopenharmony_ci				goto pio_screwed;
64262306a36Sopenharmony_ci	for (p = pio->curr_tx; p; p = p->next_data)
64362306a36Sopenharmony_ci		for (i = 0; i < n; i++)
64462306a36Sopenharmony_ci			if (p == &xfer[i])
64562306a36Sopenharmony_ci				goto pio_screwed;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/*
64862306a36Sopenharmony_ci	 * The command was completed, or wasn't yet submitted.
64962306a36Sopenharmony_ci	 * Unlink it from the que if the later.
65062306a36Sopenharmony_ci	 */
65162306a36Sopenharmony_ci	p_prev_next = &pio->curr_xfer;
65262306a36Sopenharmony_ci	for (p = pio->curr_xfer; p; p = p->next_xfer) {
65362306a36Sopenharmony_ci		if (p == &xfer[0]) {
65462306a36Sopenharmony_ci			*p_prev_next = xfer[n - 1].next_xfer;
65562306a36Sopenharmony_ci			break;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci		p_prev_next = &p->next_xfer;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/* return true if we actually unqueued something */
66162306a36Sopenharmony_ci	return !!p;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cipio_screwed:
66462306a36Sopenharmony_ci	/*
66562306a36Sopenharmony_ci	 * Life is tough. We must invalidate the hardware state and
66662306a36Sopenharmony_ci	 * discard everything that is still queued.
66762306a36Sopenharmony_ci	 */
66862306a36Sopenharmony_ci	for (p = pio->curr_resp; p; p = p->next_resp) {
66962306a36Sopenharmony_ci		p->response = FIELD_PREP(RESP_ERR_FIELD, RESP_ERR_HC_TERMINATED);
67062306a36Sopenharmony_ci		if (p->completion)
67162306a36Sopenharmony_ci			complete(p->completion);
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci	for (p = pio->curr_xfer; p; p = p->next_xfer) {
67462306a36Sopenharmony_ci		p->response = FIELD_PREP(RESP_ERR_FIELD, RESP_ERR_HC_TERMINATED);
67562306a36Sopenharmony_ci		if (p->completion)
67662306a36Sopenharmony_ci			complete(p->completion);
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	pio->curr_xfer = pio->curr_rx = pio->curr_tx = pio->curr_resp = NULL;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return true;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic bool hci_pio_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct hci_pio_data *pio = hci->io_data;
68662306a36Sopenharmony_ci	int ret;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	spin_lock_irq(&pio->lock);
68962306a36Sopenharmony_ci	DBG("n=%d status=%#x/%#x", n,
69062306a36Sopenharmony_ci	    pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
69162306a36Sopenharmony_ci	DBG("main_status = %#x/%#x",
69262306a36Sopenharmony_ci	    readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28));
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	ret = hci_pio_dequeue_xfer_common(hci, pio, xfer, n);
69562306a36Sopenharmony_ci	spin_unlock_irq(&pio->lock);
69662306a36Sopenharmony_ci	return ret;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void hci_pio_err(struct i3c_hci *hci, struct hci_pio_data *pio,
70062306a36Sopenharmony_ci			u32 status)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	/* TODO: this ought to be more sophisticated eventually */
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (pio_reg_read(INTR_STATUS) & STAT_RESP_READY) {
70562306a36Sopenharmony_ci		/* this may happen when an error is signaled with ROC unset */
70662306a36Sopenharmony_ci		u32 resp = pio_reg_read(RESPONSE_QUEUE_PORT);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		dev_err(&hci->master.dev,
70962306a36Sopenharmony_ci			"orphan response (%#x) on error\n", resp);
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* dump states on programming errors */
71362306a36Sopenharmony_ci	if (status & STAT_PROG_ERRORS) {
71462306a36Sopenharmony_ci		u32 queue = pio_reg_read(QUEUE_CUR_STATUS);
71562306a36Sopenharmony_ci		u32 data = pio_reg_read(DATA_BUFFER_CUR_STATUS);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		dev_err(&hci->master.dev,
71862306a36Sopenharmony_ci			"prog error %#lx (C/R/I = %ld/%ld/%ld, TX/RX = %ld/%ld)\n",
71962306a36Sopenharmony_ci			status & STAT_PROG_ERRORS,
72062306a36Sopenharmony_ci			FIELD_GET(CUR_CMD_Q_EMPTY_LEVEL, queue),
72162306a36Sopenharmony_ci			FIELD_GET(CUR_RESP_Q_LEVEL, queue),
72262306a36Sopenharmony_ci			FIELD_GET(CUR_IBI_Q_LEVEL, queue),
72362306a36Sopenharmony_ci			FIELD_GET(CUR_TX_BUF_LVL, data),
72462306a36Sopenharmony_ci			FIELD_GET(CUR_RX_BUF_LVL, data));
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* just bust out everything with pending responses for now */
72862306a36Sopenharmony_ci	hci_pio_dequeue_xfer_common(hci, pio, pio->curr_resp, 1);
72962306a36Sopenharmony_ci	/* ... and half-way TX transfers if any */
73062306a36Sopenharmony_ci	if (pio->curr_tx && pio->curr_tx->data_left != pio->curr_tx->data_len)
73162306a36Sopenharmony_ci		hci_pio_dequeue_xfer_common(hci, pio, pio->curr_tx, 1);
73262306a36Sopenharmony_ci	/* then reset the hardware */
73362306a36Sopenharmony_ci	mipi_i3c_hci_pio_reset(hci);
73462306a36Sopenharmony_ci	mipi_i3c_hci_resume(hci);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	DBG("status=%#x/%#x",
73762306a36Sopenharmony_ci	    pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void hci_pio_set_ibi_thresh(struct i3c_hci *hci,
74162306a36Sopenharmony_ci				   struct hci_pio_data *pio,
74262306a36Sopenharmony_ci				   unsigned int thresh_val)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	u32 regval = pio->reg_queue_thresh;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	regval &= ~QUEUE_IBI_STATUS_THLD;
74762306a36Sopenharmony_ci	regval |= FIELD_PREP(QUEUE_IBI_STATUS_THLD, thresh_val);
74862306a36Sopenharmony_ci	/* write the threshold reg only if it changes */
74962306a36Sopenharmony_ci	if (regval != pio->reg_queue_thresh) {
75062306a36Sopenharmony_ci		pio_reg_write(QUEUE_THLD_CTRL, regval);
75162306a36Sopenharmony_ci		pio->reg_queue_thresh = regval;
75262306a36Sopenharmony_ci		DBG("%d", thresh_val);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic bool hci_pio_get_ibi_segment(struct i3c_hci *hci,
75762306a36Sopenharmony_ci				    struct hci_pio_data *pio)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct hci_pio_ibi_data *ibi = &pio->ibi;
76062306a36Sopenharmony_ci	unsigned int nr_words, thresh_val;
76162306a36Sopenharmony_ci	u32 *p;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	p = ibi->data_ptr;
76462306a36Sopenharmony_ci	p += (ibi->seg_len - ibi->seg_cnt) / 4;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	while ((nr_words = ibi->seg_cnt/4)) {
76762306a36Sopenharmony_ci		/* determine our IBI queue threshold value */
76862306a36Sopenharmony_ci		thresh_val = min(nr_words, pio->max_ibi_thresh);
76962306a36Sopenharmony_ci		hci_pio_set_ibi_thresh(hci, pio, thresh_val);
77062306a36Sopenharmony_ci		/* bail out if we don't have that amount of data ready */
77162306a36Sopenharmony_ci		if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
77262306a36Sopenharmony_ci			return false;
77362306a36Sopenharmony_ci		/* extract the data from the IBI port */
77462306a36Sopenharmony_ci		nr_words = thresh_val;
77562306a36Sopenharmony_ci		ibi->seg_cnt -= nr_words * 4;
77662306a36Sopenharmony_ci		DBG("now %d left %d", nr_words * 4, ibi->seg_cnt);
77762306a36Sopenharmony_ci		while (nr_words--)
77862306a36Sopenharmony_ci			*p++ = pio_reg_read(IBI_PORT);
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (ibi->seg_cnt) {
78262306a36Sopenharmony_ci		/*
78362306a36Sopenharmony_ci		 * There are trailing bytes in the last word.
78462306a36Sopenharmony_ci		 * Fetch it and extract bytes in an endian independent way.
78562306a36Sopenharmony_ci		 * Unlike the TX case, we must not write past the end of
78662306a36Sopenharmony_ci		 * the destination buffer.
78762306a36Sopenharmony_ci		 */
78862306a36Sopenharmony_ci		u32 data;
78962306a36Sopenharmony_ci		u8 *p_byte = (u8 *)p;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		hci_pio_set_ibi_thresh(hci, pio, 1);
79262306a36Sopenharmony_ci		if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
79362306a36Sopenharmony_ci			return false;
79462306a36Sopenharmony_ci		DBG("trailing %d", ibi->seg_cnt);
79562306a36Sopenharmony_ci		data = pio_reg_read(IBI_PORT);
79662306a36Sopenharmony_ci		data = (__force u32) cpu_to_le32(data);
79762306a36Sopenharmony_ci		while (ibi->seg_cnt--) {
79862306a36Sopenharmony_ci			*p_byte++ = data;
79962306a36Sopenharmony_ci			data >>= 8;
80062306a36Sopenharmony_ci		}
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	return true;
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct hci_pio_ibi_data *ibi = &pio->ibi;
80962306a36Sopenharmony_ci	struct i3c_dev_desc *dev;
81062306a36Sopenharmony_ci	struct i3c_hci_dev_data *dev_data;
81162306a36Sopenharmony_ci	struct hci_pio_dev_ibi_data *dev_ibi;
81262306a36Sopenharmony_ci	u32 ibi_status;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/*
81562306a36Sopenharmony_ci	 * We have a new IBI. Try to set up its payload retrieval.
81662306a36Sopenharmony_ci	 * When returning true, the IBI data has to be consumed whether
81762306a36Sopenharmony_ci	 * or not we are set up to capture it. If we return true with
81862306a36Sopenharmony_ci	 * ibi->slot == NULL that means the data payload has to be
81962306a36Sopenharmony_ci	 * drained out of the IBI port and dropped.
82062306a36Sopenharmony_ci	 */
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	ibi_status = pio_reg_read(IBI_PORT);
82362306a36Sopenharmony_ci	DBG("status = %#x", ibi_status);
82462306a36Sopenharmony_ci	ibi->addr = FIELD_GET(IBI_TARGET_ADDR, ibi_status);
82562306a36Sopenharmony_ci	if (ibi_status & IBI_ERROR) {
82662306a36Sopenharmony_ci		dev_err(&hci->master.dev, "IBI error from %#x\n", ibi->addr);
82762306a36Sopenharmony_ci		return false;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	ibi->last_seg = ibi_status & IBI_LAST_STATUS;
83162306a36Sopenharmony_ci	ibi->seg_len = FIELD_GET(IBI_DATA_LENGTH, ibi_status);
83262306a36Sopenharmony_ci	ibi->seg_cnt = ibi->seg_len;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	dev = i3c_hci_addr_to_dev(hci, ibi->addr);
83562306a36Sopenharmony_ci	if (!dev) {
83662306a36Sopenharmony_ci		dev_err(&hci->master.dev,
83762306a36Sopenharmony_ci			"IBI for unknown device %#x\n", ibi->addr);
83862306a36Sopenharmony_ci		return true;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	dev_data = i3c_dev_get_master_data(dev);
84262306a36Sopenharmony_ci	dev_ibi = dev_data->ibi_data;
84362306a36Sopenharmony_ci	ibi->max_len = dev_ibi->max_len;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (ibi->seg_len > ibi->max_len) {
84662306a36Sopenharmony_ci		dev_err(&hci->master.dev, "IBI payload too big (%d > %d)\n",
84762306a36Sopenharmony_ci			ibi->seg_len, ibi->max_len);
84862306a36Sopenharmony_ci		return true;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	ibi->slot = i3c_generic_ibi_get_free_slot(dev_ibi->pool);
85262306a36Sopenharmony_ci	if (!ibi->slot) {
85362306a36Sopenharmony_ci		dev_err(&hci->master.dev, "no free slot for IBI\n");
85462306a36Sopenharmony_ci	} else {
85562306a36Sopenharmony_ci		ibi->slot->len = 0;
85662306a36Sopenharmony_ci		ibi->data_ptr = ibi->slot->data;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci	return true;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic void hci_pio_free_ibi_slot(struct i3c_hci *hci, struct hci_pio_data *pio)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	struct hci_pio_ibi_data *ibi = &pio->ibi;
86462306a36Sopenharmony_ci	struct hci_pio_dev_ibi_data *dev_ibi;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (ibi->slot) {
86762306a36Sopenharmony_ci		dev_ibi = ibi->slot->dev->common.master_priv;
86862306a36Sopenharmony_ci		i3c_generic_ibi_recycle_slot(dev_ibi->pool, ibi->slot);
86962306a36Sopenharmony_ci		ibi->slot = NULL;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic bool hci_pio_process_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct hci_pio_ibi_data *ibi = &pio->ibi;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!ibi->slot && !ibi->seg_cnt && ibi->last_seg)
87862306a36Sopenharmony_ci		if (!hci_pio_prep_new_ibi(hci, pio))
87962306a36Sopenharmony_ci			return false;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	for (;;) {
88262306a36Sopenharmony_ci		u32 ibi_status;
88362306a36Sopenharmony_ci		unsigned int ibi_addr;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		if (ibi->slot) {
88662306a36Sopenharmony_ci			if (!hci_pio_get_ibi_segment(hci, pio))
88762306a36Sopenharmony_ci				return false;
88862306a36Sopenharmony_ci			ibi->slot->len += ibi->seg_len;
88962306a36Sopenharmony_ci			ibi->data_ptr += ibi->seg_len;
89062306a36Sopenharmony_ci			if (ibi->last_seg) {
89162306a36Sopenharmony_ci				/* was the last segment: submit it and leave */
89262306a36Sopenharmony_ci				i3c_master_queue_ibi(ibi->slot->dev, ibi->slot);
89362306a36Sopenharmony_ci				ibi->slot = NULL;
89462306a36Sopenharmony_ci				hci_pio_set_ibi_thresh(hci, pio, 1);
89562306a36Sopenharmony_ci				return true;
89662306a36Sopenharmony_ci			}
89762306a36Sopenharmony_ci		} else if (ibi->seg_cnt) {
89862306a36Sopenharmony_ci			/*
89962306a36Sopenharmony_ci			 * No slot but a non-zero count. This is the result
90062306a36Sopenharmony_ci			 * of some error and the payload must be drained.
90162306a36Sopenharmony_ci			 * This normally does not happen therefore no need
90262306a36Sopenharmony_ci			 * to be extra optimized here.
90362306a36Sopenharmony_ci			 */
90462306a36Sopenharmony_ci			hci_pio_set_ibi_thresh(hci, pio, 1);
90562306a36Sopenharmony_ci			do {
90662306a36Sopenharmony_ci				if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
90762306a36Sopenharmony_ci					return false;
90862306a36Sopenharmony_ci				pio_reg_read(IBI_PORT);
90962306a36Sopenharmony_ci			} while (--ibi->seg_cnt);
91062306a36Sopenharmony_ci			if (ibi->last_seg)
91162306a36Sopenharmony_ci				return true;
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		/* try to move to the next segment right away */
91562306a36Sopenharmony_ci		hci_pio_set_ibi_thresh(hci, pio, 1);
91662306a36Sopenharmony_ci		if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
91762306a36Sopenharmony_ci			return false;
91862306a36Sopenharmony_ci		ibi_status = pio_reg_read(IBI_PORT);
91962306a36Sopenharmony_ci		ibi_addr = FIELD_GET(IBI_TARGET_ADDR, ibi_status);
92062306a36Sopenharmony_ci		if (ibi->addr != ibi_addr) {
92162306a36Sopenharmony_ci			/* target address changed before last segment */
92262306a36Sopenharmony_ci			dev_err(&hci->master.dev,
92362306a36Sopenharmony_ci				"unexp IBI address changed from %d to %d\n",
92462306a36Sopenharmony_ci				ibi->addr, ibi_addr);
92562306a36Sopenharmony_ci			hci_pio_free_ibi_slot(hci, pio);
92662306a36Sopenharmony_ci		}
92762306a36Sopenharmony_ci		ibi->last_seg = ibi_status & IBI_LAST_STATUS;
92862306a36Sopenharmony_ci		ibi->seg_len = FIELD_GET(IBI_DATA_LENGTH, ibi_status);
92962306a36Sopenharmony_ci		ibi->seg_cnt = ibi->seg_len;
93062306a36Sopenharmony_ci		if (ibi->slot && ibi->slot->len + ibi->seg_len > ibi->max_len) {
93162306a36Sopenharmony_ci			dev_err(&hci->master.dev,
93262306a36Sopenharmony_ci				"IBI payload too big (%d > %d)\n",
93362306a36Sopenharmony_ci				ibi->slot->len + ibi->seg_len, ibi->max_len);
93462306a36Sopenharmony_ci			hci_pio_free_ibi_slot(hci, pio);
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return false;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic int hci_pio_request_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev,
94262306a36Sopenharmony_ci			       const struct i3c_ibi_setup *req)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
94562306a36Sopenharmony_ci	struct i3c_generic_ibi_pool *pool;
94662306a36Sopenharmony_ci	struct hci_pio_dev_ibi_data *dev_ibi;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	dev_ibi = kmalloc(sizeof(*dev_ibi), GFP_KERNEL);
94962306a36Sopenharmony_ci	if (!dev_ibi)
95062306a36Sopenharmony_ci		return -ENOMEM;
95162306a36Sopenharmony_ci	pool = i3c_generic_ibi_alloc_pool(dev, req);
95262306a36Sopenharmony_ci	if (IS_ERR(pool)) {
95362306a36Sopenharmony_ci		kfree(dev_ibi);
95462306a36Sopenharmony_ci		return PTR_ERR(pool);
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci	dev_ibi->pool = pool;
95762306a36Sopenharmony_ci	dev_ibi->max_len = req->max_payload_len;
95862306a36Sopenharmony_ci	dev_data->ibi_data = dev_ibi;
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void hci_pio_free_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
96562306a36Sopenharmony_ci	struct hci_pio_dev_ibi_data *dev_ibi = dev_data->ibi_data;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	dev_data->ibi_data = NULL;
96862306a36Sopenharmony_ci	i3c_generic_ibi_free_pool(dev_ibi->pool);
96962306a36Sopenharmony_ci	kfree(dev_ibi);
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic void hci_pio_recycle_ibi_slot(struct i3c_hci *hci,
97362306a36Sopenharmony_ci				    struct i3c_dev_desc *dev,
97462306a36Sopenharmony_ci				    struct i3c_ibi_slot *slot)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
97762306a36Sopenharmony_ci	struct hci_pio_dev_ibi_data *dev_ibi = dev_data->ibi_data;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	i3c_generic_ibi_recycle_slot(dev_ibi->pool, slot);
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic bool hci_pio_irq_handler(struct i3c_hci *hci, unsigned int unused)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct hci_pio_data *pio = hci->io_data;
98562306a36Sopenharmony_ci	u32 status;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	spin_lock(&pio->lock);
98862306a36Sopenharmony_ci	status = pio_reg_read(INTR_STATUS);
98962306a36Sopenharmony_ci	DBG("(in) status: %#x/%#x", status, pio->enabled_irqs);
99062306a36Sopenharmony_ci	status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS;
99162306a36Sopenharmony_ci	if (!status) {
99262306a36Sopenharmony_ci		spin_unlock(&pio->lock);
99362306a36Sopenharmony_ci		return false;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (status & STAT_IBI_STATUS_THLD)
99762306a36Sopenharmony_ci		hci_pio_process_ibi(hci, pio);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (status & STAT_RX_THLD)
100062306a36Sopenharmony_ci		if (hci_pio_process_rx(hci, pio))
100162306a36Sopenharmony_ci			pio->enabled_irqs &= ~STAT_RX_THLD;
100262306a36Sopenharmony_ci	if (status & STAT_TX_THLD)
100362306a36Sopenharmony_ci		if (hci_pio_process_tx(hci, pio))
100462306a36Sopenharmony_ci			pio->enabled_irqs &= ~STAT_TX_THLD;
100562306a36Sopenharmony_ci	if (status & STAT_RESP_READY)
100662306a36Sopenharmony_ci		if (hci_pio_process_resp(hci, pio))
100762306a36Sopenharmony_ci			pio->enabled_irqs &= ~STAT_RESP_READY;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (unlikely(status & STAT_LATENCY_WARNINGS)) {
101062306a36Sopenharmony_ci		pio_reg_write(INTR_STATUS, status & STAT_LATENCY_WARNINGS);
101162306a36Sopenharmony_ci		dev_warn_ratelimited(&hci->master.dev,
101262306a36Sopenharmony_ci				     "encountered warning condition %#lx\n",
101362306a36Sopenharmony_ci				     status & STAT_LATENCY_WARNINGS);
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (unlikely(status & STAT_ALL_ERRORS)) {
101762306a36Sopenharmony_ci		pio_reg_write(INTR_STATUS, status & STAT_ALL_ERRORS);
101862306a36Sopenharmony_ci		hci_pio_err(hci, pio, status & STAT_ALL_ERRORS);
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (status & STAT_CMD_QUEUE_READY)
102262306a36Sopenharmony_ci		if (hci_pio_process_cmd(hci, pio))
102362306a36Sopenharmony_ci			pio->enabled_irqs &= ~STAT_CMD_QUEUE_READY;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
102662306a36Sopenharmony_ci	DBG("(out) status: %#x/%#x",
102762306a36Sopenharmony_ci	    pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
102862306a36Sopenharmony_ci	spin_unlock(&pio->lock);
102962306a36Sopenharmony_ci	return true;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ciconst struct hci_io_ops mipi_i3c_hci_pio = {
103362306a36Sopenharmony_ci	.init			= hci_pio_init,
103462306a36Sopenharmony_ci	.cleanup		= hci_pio_cleanup,
103562306a36Sopenharmony_ci	.queue_xfer		= hci_pio_queue_xfer,
103662306a36Sopenharmony_ci	.dequeue_xfer		= hci_pio_dequeue_xfer,
103762306a36Sopenharmony_ci	.irq_handler		= hci_pio_irq_handler,
103862306a36Sopenharmony_ci	.request_ibi		= hci_pio_request_ibi,
103962306a36Sopenharmony_ci	.free_ibi		= hci_pio_free_ibi,
104062306a36Sopenharmony_ci	.recycle_ibi_slot	= hci_pio_recycle_ibi_slot,
104162306a36Sopenharmony_ci};
1042