Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d97cbc9 authored by Girish Mahadevan's avatar Girish Mahadevan
Browse files

serial: msm_geni_serial: Modify the clock on/off sequence for HS-UART



Currently the driver leaves the Rx state machine running when it votes the
clocks off. However if the system enters certain deeper sleep modes it is
possible that the Rx state machine will not be in a known state once the
clocks are turned back on resulting in undefined behavior on the Rx path.
To counter this hardware limitation always cancel the Rx sequencer when
clocking off the core, and enable the sequencer again when turning the
clocks on.

When turning off the resource votes, modify the pinctrl settings at the
end after the clocks and bus votes are done, to avoid any glitches on the
line. Modify the inflight transfer check to include DMA mode as well.
For clients that use inband wakeup mechanism, set the active and sleep
GPIO settings to be the same since in these cases we always want the pins
connected to the core.

Change-Id: I297a6292914e4b1881202aba8e9b8151da0cac67
Signed-off-by: default avatarGirish Mahadevan <girishm@codeaurora.org>
parent 87b21110
Loading
Loading
Loading
Loading
+11 −12
Original line number Diff line number Diff line
@@ -402,7 +402,7 @@ EXPORT_SYMBOL(geni_setup_s_cmd);
 */
void geni_cancel_m_cmd(void __iomem *base)
{
	geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_S_CMD_CTRL_REG);
	geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_M_CMD_CTRL_REG);
}
EXPORT_SYMBOL(geni_cancel_m_cmd);

@@ -684,16 +684,14 @@ int se_geni_resources_off(struct se_geni_rsc *rsc)
	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
		return -ENODEV;

	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
	if (ret) {
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
			"%s: Error %d pinctrl_select_state\n", __func__, ret);
		return ret;
	}
	ret = se_geni_clks_off(rsc);
	if (ret)
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
			"%s: Error %d turning off clocks\n", __func__, ret);
	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
	if (ret)
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
			"%s: Error %d pinctrl_select_state\n", __func__, ret);
	return ret;
}
EXPORT_SYMBOL(se_geni_resources_off);
@@ -802,19 +800,20 @@ int se_geni_resources_on(struct se_geni_rsc *rsc)
	if (unlikely(!geni_se_dev))
		return -EPROBE_DEFER;

	ret = se_geni_clks_on(rsc);
	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
	if (ret) {
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
			"%s: Error %d during clks_on\n", __func__, ret);
			"%s: Error %d pinctrl_select_state\n", __func__, ret);
		return ret;
	}

	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
	ret = se_geni_clks_on(rsc);
	if (ret) {
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
			"%s: Error %d pinctrl_select_state\n", __func__, ret);
		se_geni_clks_off(rsc);
			"%s: Error %d during clks_on\n", __func__, ret);
		pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
	}

	return ret;
}
EXPORT_SYMBOL(se_geni_resources_on);
+137 −66
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ struct msm_geni_serial_port {
	int ioctl_count;
	int edge_count;
	unsigned int tx_yield_count;
	bool manual_flow;
};

static const struct uart_ops msm_geni_serial_pops;
@@ -266,16 +267,18 @@ static bool check_transfers_inflight(struct uart_port *uport)

	/* Possible stop tx is called multiple times. */
	m_cmd_active = geni_status & M_GENI_CMD_ACTIVE;
	if (port->xfer_mode == SE_DMA)
	if (port->xfer_mode == SE_DMA) {
		tx_fifo_status = port->tx_dma ? 1 : 0;
	else
		rx_fifo_status =
			geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);
	} else {
		tx_fifo_status = geni_read_reg_nolog(uport->membase,
						SE_GENI_TX_FIFO_STATUS);
	tx_active = m_cmd_active || tx_fifo_status;
		rx_fifo_status = geni_read_reg_nolog(uport->membase,
						SE_GENI_RX_FIFO_STATUS);
	if (rx_fifo_status)
		rx_active = true;
	}
	tx_active = m_cmd_active || tx_fifo_status;
	rx_active =  rx_fifo_status ? true : false;

	if (rx_active || tx_active || !uart_circ_empty(xmit))
		xfer_on = true;
@@ -303,10 +306,12 @@ static void wait_for_transfers_inflight(struct uart_port *uport)
		u32 geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
		u32 rx_fifo_status = geni_read_reg_nolog(uport->membase,
							SE_GENI_RX_FIFO_STATUS);
		u32 rx_dma =
			geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);

		IPC_LOG_MSG(port->ipc_log_misc,
			"%s IOS 0x%x geni status 0x%x rx fifo 0x%x\n",
			__func__, geni_ios, geni_status, rx_fifo_status);
			"%s IOS 0x%x geni status 0x%x rx: fifo 0x%x dma 0x%x\n",
		__func__, geni_ios, geni_status, rx_fifo_status, rx_dma);
	}
}

@@ -405,13 +410,9 @@ static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport)
{
	u32 geni_ios = 0;
	unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

	if (device_pending_suspend(uport)) {
		IPC_LOG_MSG(port->ipc_log_misc,
				"%s.Device is suspended.\n", __func__);
	if (device_pending_suspend(uport))
		return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
	}

	geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
	if (!(geni_ios & IO2_DATA_IN))
@@ -436,8 +437,12 @@ static void msm_geni_serial_set_mctrl(struct uart_port *uport,
				"%s.Device is suspended.\n", __func__);
		return;
	}
	if (!(mctrl & TIOCM_RTS))
	if (!(mctrl & TIOCM_RTS)) {
		uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_NOT_READY);
		port->manual_flow = true;
	} else {
		port->manual_flow = false;
	}
	geni_write_reg_nolog(uart_manual_rfr, uport->membase,
							SE_UART_MANUAL_RFR);
	/* Write to flow control must complete before return to client*/
@@ -542,7 +547,7 @@ static int msm_geni_serial_poll_bit(struct uart_port *uport,
		 * Total polling iterations based on FIFO worth of bytes to be
		 * sent at current baud .Add a little fluff to the wait.
		 */
		total_iter = ((fifo_bits * USEC_PER_SEC) / baud);
		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
		total_iter += 50;
	}

@@ -920,19 +925,12 @@ static void msm_geni_serial_tx_fsm_rst(struct uart_port *uport)
	geni_write_reg_nolog(tx_irq_en, uport->membase, SE_DMA_TX_IRQ_EN_SET);
}

static void msm_geni_serial_stop_tx(struct uart_port *uport)
static void stop_tx_sequencer(struct uart_port *uport)
{
	unsigned int geni_m_irq_en;
	unsigned int geni_status;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

	if (!uart_console(uport) && device_pending_suspend(uport)) {
		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
		IPC_LOG_MSG(port->ipc_log_misc,
				"%s.Device is suspended.\n", __func__);
		return;
	}

	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
	geni_m_irq_en &= ~M_CMD_DONE_EN;
	if (port->xfer_mode == FIFO_MODE) {
@@ -948,9 +946,7 @@ static void msm_geni_serial_stop_tx(struct uart_port *uport)
		}
	}
	port->xmit_size = 0;

	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);

	geni_status = geni_read_reg_nolog(uport->membase,
						SE_GENI_STATUS);
	/* Possible stop tx is called multiple times. */
@@ -970,13 +966,9 @@ static void msm_geni_serial_stop_tx(struct uart_port *uport)
	IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
}

static void msm_geni_serial_start_rx(struct uart_port *uport)
static void msm_geni_serial_stop_tx(struct uart_port *uport)
{
	unsigned int geni_s_irq_en;
	unsigned int geni_m_irq_en;
	unsigned int geni_status;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
	int ret;

	if (!uart_console(uport) && device_pending_suspend(uport)) {
		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
@@ -984,6 +976,16 @@ static void msm_geni_serial_start_rx(struct uart_port *uport)
				"%s.Device is suspended.\n", __func__);
		return;
	}
	stop_tx_sequencer(uport);
}

static void start_rx_sequencer(struct uart_port *uport)
{
	unsigned int geni_s_irq_en;
	unsigned int geni_m_irq_en;
	unsigned int geni_status;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
	int ret;

	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
	if (geni_status & S_GENI_CMD_ACTIVE)
@@ -1011,7 +1013,7 @@ static void msm_geni_serial_start_rx(struct uart_port *uport)
			dev_err(uport->dev, "%s: RX Prep dma failed %d\n",
				__func__, ret);
			msm_geni_serial_stop_rx(uport);
			goto exit_geni_serial_start_rx;
			goto exit_start_rx_sequencer;
		}
	}
	/*
@@ -1020,10 +1022,24 @@ static void msm_geni_serial_start_rx(struct uart_port *uport)
	 */
	mb();
	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
exit_geni_serial_start_rx:
exit_start_rx_sequencer:
	IPC_LOG_MSG(port->ipc_log_misc, "%s 0x%x\n", __func__, geni_status);
}

static void msm_geni_serial_start_rx(struct uart_port *uport)
{
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

	if (!uart_console(uport) && device_pending_suspend(uport)) {
		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
		IPC_LOG_MSG(port->ipc_log_misc,
				"%s.Device is suspended.\n", __func__);
		return;
	}
	start_rx_sequencer(&port->uport);
}


static void msm_geni_serial_rx_fsm_rst(struct uart_port *uport)
{
	unsigned int rx_irq_en;
@@ -1043,19 +1059,15 @@ static void msm_geni_serial_rx_fsm_rst(struct uart_port *uport)
	geni_write_reg_nolog(rx_irq_en, uport->membase, SE_DMA_RX_IRQ_EN_SET);
}

static void msm_geni_serial_stop_rx(struct uart_port *uport)
static void stop_rx_sequencer(struct uart_port *uport)
{
	unsigned int geni_s_irq_en;
	unsigned int geni_m_irq_en;
	unsigned int geni_status;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
	u32 irq_clear = S_CMD_DONE_EN;
	bool done;

	if (!uart_console(uport) && device_pending_suspend(uport)) {
		IPC_LOG_MSG(port->ipc_log_misc,
				"%s.Device is suspended.\n", __func__);
		return;
	}
	IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
	if (port->xfer_mode == FIFO_MODE) {
		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
@@ -1069,28 +1081,47 @@ static void msm_geni_serial_stop_rx(struct uart_port *uport)
							SE_GENI_S_IRQ_EN);
		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
							SE_GENI_M_IRQ_EN);
	} else if (port->xfer_mode == SE_DMA && port->rx_dma) {
		msm_geni_serial_rx_fsm_rst(uport);
		geni_se_rx_dma_unprep(port->wrapper_dev, port->rx_dma,
						      DMA_RX_BUF_SIZE);
		port->rx_dma = (dma_addr_t)NULL;
	}

	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
	/* Possible stop rx is called multiple times. */
	if (!(geni_status & S_GENI_CMD_ACTIVE))
		return;
		goto exit_rx_seq;
	geni_cancel_s_cmd(uport->membase);
	/*
	 * Ensure that the cancel goes through before polling for the
	 * cancel control bit.
	 */
	mb();
	msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
	done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
					S_GENI_CMD_CANCEL, false);
	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
	if (!done)
		IPC_LOG_MSG(port->ipc_log_misc, "%s Cancel fail 0x%x\n",
							__func__, geni_status);

	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
	if ((geni_status & S_GENI_CMD_ACTIVE))
		msm_geni_serial_abort_rx(uport);
exit_rx_seq:
	if (port->xfer_mode == SE_DMA && port->rx_dma) {
		msm_geni_serial_rx_fsm_rst(uport);
		geni_se_rx_dma_unprep(port->wrapper_dev, port->rx_dma,
						      DMA_RX_BUF_SIZE);
		port->rx_dma = (dma_addr_t)NULL;
	}
}

static void msm_geni_serial_stop_rx(struct uart_port *uport)
{
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

	if (!uart_console(uport) && device_pending_suspend(uport)) {
		IPC_LOG_MSG(port->ipc_log_misc,
				"%s.Device is suspended.\n", __func__);
		return;
	}
	stop_rx_sequencer(uport);
}

static int handle_rx_hs(struct uart_port *uport,
@@ -1890,7 +1921,7 @@ static unsigned int msm_geni_serial_tx_empty(struct uart_port *uport)
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

	if (!uart_console(uport) && device_pending_suspend(uport))
		return 0;
		return 1;

	if (port->xfer_mode == SE_DMA)
		tx_fifo_status = port->tx_dma ? 1 : 0;
@@ -2377,6 +2408,11 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
		goto exit_geni_serial_probe;
	}

	/* Optional to use the Rx pin as wakeup irq */
	dev_port->wakeup_irq = platform_get_irq(pdev, 1);
	if ((dev_port->wakeup_irq < 0 && !is_console))
		dev_info(&pdev->dev, "No wakeup IRQ configured\n");

	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
		dev_err(&pdev->dev, "No pinctrl config specified!\n");
@@ -2391,6 +2427,16 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
		goto exit_geni_serial_probe;
	}

	/*
	 * For clients who setup an Inband wakeup, leave the GPIO pins
	 * always connected to the core, else move the pins to their
	 * defined "sleep" state.
	 */
	if (dev_port->wakeup_irq > 0) {
		dev_port->serial_rsc.geni_gpio_sleep =
			dev_port->serial_rsc.geni_gpio_active;
	} else {
		dev_port->serial_rsc.geni_gpio_sleep =
			pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
							PINCTRL_SLEEP);
@@ -2399,6 +2445,7 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
			ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
			goto exit_geni_serial_probe;
		}
	}

	wakeup_source_init(&dev_port->geni_wake, dev_name(&pdev->dev));
	dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
@@ -2414,11 +2461,6 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
		goto exit_geni_serial_probe;
	}

	/* Optional to use the Rx pin as wakeup irq */
	dev_port->wakeup_irq = platform_get_irq(pdev, 1);
	if ((dev_port->wakeup_irq < 0 && !is_console))
		dev_info(&pdev->dev, "No wakeup IRQ configured\n");

	uport->private_data = (void *)drv;
	platform_set_drvdata(pdev, dev_port);
	if (is_console) {
@@ -2462,25 +2504,42 @@ static int msm_geni_serial_runtime_suspend(struct device *dev)
	struct platform_device *pdev = to_platform_device(dev);
	struct msm_geni_serial_port *port = platform_get_drvdata(pdev);
	int ret = 0;
	u32 uart_manual_rfr = 0;
	u32 geni_status = geni_read_reg_nolog(port->uport.membase,
							SE_GENI_STATUS);

	wait_for_transfers_inflight(&port->uport);
	/*
	 * Disable Interrupt
	 * Manual RFR On.
	 * Stop Rx.
	 * Resources off
	 */
	disable_irq(port->uport.irq);
	/*
	 * If the clients haven't done a manual flow on/off then go ahead and
	 * set this to manual flow on.
	 */
	if (!port->manual_flow) {
		uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_READY);
		geni_write_reg_nolog(uart_manual_rfr, port->uport.membase,
							SE_UART_MANUAL_RFR);
		/*
		 * Ensure that the manual flow on writes go through before
		 * doing a stop_rx else we could end up flowing off the peer.
		 */
		mb();
	}
	stop_rx_sequencer(&port->uport);
	if ((geni_status & M_GENI_CMD_ACTIVE))
		stop_tx_sequencer(&port->uport);
	ret = se_geni_resources_off(&port->serial_rsc);
	if (ret) {
		dev_err(dev, "%s: Error ret %d\n", __func__, ret);
		goto exit_runtime_suspend;
	}
	if (port->wakeup_irq > 0) {
		struct se_geni_rsc *rsc = &port->serial_rsc;

		port->edge_count = 0;
		ret = pinctrl_select_state(rsc->geni_pinctrl,
						rsc->geni_gpio_active);
		if (ret) {
			dev_err(dev, "%s: Error %d pinctrl_select_state\n",
							__func__, ret);
			goto exit_runtime_suspend;
		}
		enable_irq(port->wakeup_irq);
	}
	IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__);
@@ -2503,12 +2562,24 @@ static int msm_geni_serial_runtime_resume(struct device *dev)
	__pm_stay_awake(&port->geni_wake);
	if (port->wakeup_irq > 0)
		disable_irq(port->wakeup_irq);
	/*
	 * Resources On.
	 * Start Rx.
	 * Auto RFR.
	 * Enable IRQ.
	 */
	ret = se_geni_resources_on(&port->serial_rsc);
	if (ret) {
		dev_err(dev, "%s: Error ret %d\n", __func__, ret);
		__pm_relax(&port->geni_wake);
		goto exit_runtime_resume;
	}
	start_rx_sequencer(&port->uport);
	if (!port->manual_flow)
		geni_write_reg_nolog(0, port->uport.membase,
						SE_UART_MANUAL_RFR);
	/* Ensure that the Rx is running before enabling interrupts */
	mb();
	enable_irq(port->uport.irq);
	IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__);
exit_runtime_resume: