| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <console/console.h> |
| #include <soc/qup_se_handlers_common.h> |
| #include <soc/qcom_qup_se.h> |
| |
| 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, |
| struct stopwatch *timeout) |
| { |
| unsigned int m_irq; |
| unsigned int rx_rem_bytes = din ? size : 0; |
| unsigned int tx_rem_bytes = dout ? size : 0; |
| struct qup_regs *regs = qup[bus].regs; |
| |
| 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(timeout)); |
| |
| 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; |
| } |