/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include u32 qup_wait_for_m_irq(unsigned int bus) { struct stopwatch sw; unsigned int m_irq = 0; struct qup_regs *regs = qup[bus].regs; stopwatch_init_usecs_expire(&sw, 25); while (!stopwatch_expired(&sw)) { m_irq = read32(®s->geni_m_irq_status); if (m_irq) break; } return m_irq; } u32 qup_wait_for_s_irq(unsigned int bus) { struct stopwatch sw; unsigned int s_irq = 0; struct qup_regs *regs = qup[bus].regs; stopwatch_init_usecs_expire(&sw, 25); while (!stopwatch_expired(&sw)) { s_irq = read32(®s->geni_s_irq_status); if (s_irq) break; } return s_irq; } static int handle_tx(unsigned int bus, const u8 *dout, unsigned int tx_rem_bytes) { int max_bytes = 0; struct qup_regs *regs = qup[bus].regs; max_bytes = (FIFO_DEPTH - TX_WATERMARK) * BYTES_PER_FIFO_WORD; max_bytes = MIN(tx_rem_bytes, max_bytes); buffer_to_fifo32((void *)dout, max_bytes, ®s->geni_tx_fifon, 0, BYTES_PER_FIFO_WORD); if (tx_rem_bytes == max_bytes) write32(®s->geni_tx_watermark_reg, 0); return max_bytes; } static int handle_rx(unsigned int bus, u8 *din, unsigned int rx_rem_bytes) { struct qup_regs *regs = qup[bus].regs; u32 rx_fifo_status = read32(®s->geni_rx_fifo_status); int rx_bytes = 0; rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * BYTES_PER_FIFO_WORD; rx_bytes = MIN(rx_rem_bytes, rx_bytes); buffer_from_fifo32(din, rx_bytes, ®s->geni_rx_fifon, 0, BYTES_PER_FIFO_WORD); return rx_bytes; } void qup_m_cancel_and_abort(unsigned int bus) { struct qup_regs *regs = qup[bus].regs; struct stopwatch sw; unsigned int m_irq; write32(®s->geni_tx_watermark_reg, 0); write32(®s->geni_m_cmd_ctrl_reg, M_GENI_CMD_CANCEL); stopwatch_init_msecs_expire(&sw, 100); do { m_irq = qup_wait_for_m_irq(bus); if (m_irq & M_CMD_CANCEL_EN) { write32(®s->geni_m_irq_clear, m_irq); break; } write32(®s->geni_m_irq_clear, m_irq); } while (!stopwatch_expired(&sw)); if (!(m_irq & M_CMD_CANCEL_EN)) { printk(BIOS_INFO, "%s:Cancel failed, Abort the operation\n", __func__); write32(®s->geni_m_cmd_ctrl_reg, M_GENI_CMD_ABORT); stopwatch_init_msecs_expire(&sw, 100); do { m_irq = qup_wait_for_m_irq(bus); if (m_irq & M_CMD_ABORT_EN) { write32(®s->geni_m_irq_clear, m_irq); break; } write32(®s->geni_m_irq_clear, m_irq); } while (!stopwatch_expired(&sw)); if (!(m_irq & M_CMD_ABORT_EN)) printk(BIOS_INFO, "%s:Abort failed\n", __func__); } } void qup_s_cancel_and_abort(unsigned int bus) { struct qup_regs *regs = qup[bus].regs; struct stopwatch sw; unsigned int s_irq; u32 rx_fifo_status; u8 buf[64]; /* FIFO size */ write32(®s->geni_tx_watermark_reg, 0); write32(®s->geni_s_cmd_ctrl_reg, S_GENI_CMD_CANCEL); stopwatch_init_msecs_expire(&sw, 100); do { s_irq = qup_wait_for_s_irq(bus); rx_fifo_status = read32(®s->geni_rx_fifo_status); if (rx_fifo_status & RX_LAST) /* Read whatever data available in FIFO */ handle_rx(bus, buf, 64); if (s_irq & S_CMD_CANCEL_EN) { write32(®s->geni_s_irq_clear, s_irq); break; } write32(®s->geni_s_irq_clear, s_irq); } while (!stopwatch_expired(&sw)); if (!(s_irq & S_CMD_CANCEL_EN)) { printk(BIOS_INFO, "%s:Cancel failed, Abort the operation\n", __func__); write32(®s->geni_s_cmd_ctrl_reg, S_GENI_CMD_ABORT); stopwatch_init_msecs_expire(&sw, 100); do { s_irq = qup_wait_for_s_irq(bus); if (s_irq & S_CMD_ABORT_EN) { write32(®s->geni_s_irq_clear, s_irq); break; } write32(®s->geni_s_irq_clear, s_irq); } while (!stopwatch_expired(&sw)); if (!(s_irq & S_CMD_ABORT_EN)) printk(BIOS_INFO, "%s:Abort failed\n", __func__); } } int qup_handle_transfer(unsigned int bus, const void *dout, void *din, int size) { unsigned int m_irq; struct stopwatch sw; unsigned int rx_rem_bytes = din ? size : 0; unsigned int tx_rem_bytes = dout ? size : 0; struct qup_regs *regs = qup[bus].regs; stopwatch_init_msecs_expire(&sw, 1000); do { m_irq = qup_wait_for_m_irq(bus); if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN)) rx_rem_bytes -= handle_rx(bus, din + size - rx_rem_bytes, rx_rem_bytes); if (m_irq & M_TX_FIFO_WATERMARK_EN) tx_rem_bytes -= handle_tx(bus, dout + size - tx_rem_bytes, tx_rem_bytes); if (m_irq & M_CMD_DONE_EN) { write32(®s->geni_m_irq_clear, m_irq); break; } write32(®s->geni_m_irq_clear, m_irq); } while (!stopwatch_expired(&sw)); if (!(m_irq & M_CMD_DONE_EN) || tx_rem_bytes || rx_rem_bytes) { printk(BIOS_INFO, "%s:Error: Transfer failed\n", __func__); qup_m_cancel_and_abort(bus); return -1; } return 0; }