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