Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 82 additions & 9 deletions drivers/serial/serial_msm_geni.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,70 @@ static void qcom_geni_serial_abort_rx(struct udevice *dev)
writel(FORCE_DEFAULT, priv->base + GENI_FORCE_DEFAULT_REG);
}

static void qcom_geni_serial_stop_rx_fifo(struct udevice *dev)
{
struct msm_serial_data *priv = dev_get_priv(dev);
u32 irq_en;
u32 s_irq_status;
u32 status;
u32 rx_fifo_status;

/* Disable RX interrupts */
irq_en = readl(priv->base + SE_GENI_S_IRQ_EN);
irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
writel(irq_en, priv->base + SE_GENI_S_IRQ_EN);

irq_en = readl(priv->base + SE_GENI_M_IRQ_EN);
irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
writel(irq_en, priv->base + SE_GENI_M_IRQ_EN);

/* Check if secondary sequencer is active */
status = readl(priv->base + SE_GENI_STATUS);
if (!(status & S_GENI_CMD_ACTIVE))
return;

/* Send CANCEL command */
writel(S_GENI_CMD_CANCEL, priv->base + SE_GENI_S_CMD_CTRL_REG);

/* Poll for CANCEL completion */
qcom_geni_serial_poll_bit(dev, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_CANCEL, false);

/* Read IRQ status and check if S_RX_FIFO_LAST_EN is set */
s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS);

/* Flush/drain the RX FIFO if needed */
if (s_irq_status & S_RX_FIFO_LAST_EN) {
while ((rx_fifo_status = readl(priv->base + SE_GENI_RX_FIFO_STATUS)) &
RX_FIFO_WC_MSK) {
readl(priv->base + SE_GENI_RX_FIFOn);
}
}

/* Clear all pending IRQ status bits */
writel(s_irq_status, priv->base + SE_GENI_S_IRQ_CLEAR);

/* Check if still active after CANCEL */
status = readl(priv->base + SE_GENI_STATUS);
if (status & S_GENI_CMD_ACTIVE) {
/* Send ABORT as fallback */
writel(S_GENI_CMD_ABORT, priv->base + SE_GENI_S_CMD_CTRL_REG);
qcom_geni_serial_poll_bit(dev, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_ABORT, false);

/* Clear ABORT IRQ status */
s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS);
writel(s_irq_status, priv->base + SE_GENI_S_IRQ_CLEAR);
}

writel(FORCE_DEFAULT, priv->base + GENI_FORCE_DEFAULT_REG);
}

static void msm_geni_serial_setup_rx(struct udevice *dev)
{
struct msm_serial_data *priv = dev_get_priv(dev);

qcom_geni_serial_abort_rx(dev);
qcom_geni_serial_stop_rx_fifo(dev);

writel(UART_PACKING_CFG0, priv->base + SE_GENI_RX_PACKING_CFG0);
writel(UART_PACKING_CFG1, priv->base + SE_GENI_RX_PACKING_CFG1);
Expand Down Expand Up @@ -454,15 +513,29 @@ static int msm_serial_getc(struct udevice *dev)
static int msm_serial_pending(struct udevice *dev, bool input)
{
struct msm_serial_data *priv = dev_get_priv(dev);
u32 fifo_status;
u32 s_irq_status;
int word_count;

if (input)
return readl(priv->base + SE_GENI_RX_FIFO_STATUS) &
RX_FIFO_WC_MSK;
else
return readl(priv->base + SE_GENI_TX_FIFO_STATUS) &
TX_FIFO_WC_MSK;
if (input) {
fifo_status = readl(priv->base + SE_GENI_RX_FIFO_STATUS);
word_count = fifo_status & RX_FIFO_WC_MSK;

return 0;
if (word_count > 0) {
/* Validate RX FIFO data with IRQ status */
s_irq_status = readl(priv->base + SE_GENI_S_IRQ_STATUS);

if (!(s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN)))
return 0;
}

return word_count;
} else {
fifo_status = readl(priv->base + SE_GENI_TX_FIFO_STATUS);
word_count = fifo_status & TX_FIFO_WC_MSK;

return word_count;
}
}

static const struct dm_serial_ops msm_serial_ops = {
Expand Down Expand Up @@ -524,7 +597,7 @@ static inline void geni_serial_init(struct udevice *dev)
* it else we could end up in data loss scenarios.
*/
qcom_geni_serial_poll_tx_done(dev);
qcom_geni_serial_abort_rx(dev);
qcom_geni_serial_stop_rx_fifo(dev);

writel(UART_PACKING_CFG0, base_address + SE_GENI_TX_PACKING_CFG0);
writel(UART_PACKING_CFG1, base_address + SE_GENI_TX_PACKING_CFG1);
Expand Down
Loading