blob: 7dd4f452af40c813a55bf9d70c08e5d658400e14 [file] [log] [blame]
/* 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(&regs->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(&regs->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, &regs->geni_tx_fifon,
0, BYTES_PER_FIFO_WORD);
if (tx_rem_bytes == max_bytes)
write32(&regs->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(&regs->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, &regs->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(&regs->geni_tx_watermark_reg, 0);
write32(&regs->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(&regs->geni_m_irq_clear, m_irq);
break;
}
write32(&regs->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(&regs->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(&regs->geni_m_irq_clear, m_irq);
break;
}
write32(&regs->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(&regs->geni_tx_watermark_reg, 0);
write32(&regs->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(&regs->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(&regs->geni_s_irq_clear, s_irq);
break;
}
write32(&regs->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(&regs->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(&regs->geni_s_irq_clear, s_irq);
break;
}
write32(&regs->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(&regs->geni_m_irq_clear, m_irq);
break;
}
write32(&regs->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;
}