Re: [v2,8/8] tty: serial: qcom_geni_serial: Add early console support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Apr 09, 2018 at 01:38:41PM -0600, Karthikeyan Ramasubramanian wrote:
> Add early console support in Qualcomm Technologies Inc., GENI based
> UART controller.
> 
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@xxxxxxxxxxxxxx>
> Signed-off-by: Girish Mahadevan <girishm@xxxxxxxxxxxxxx>
> ---
>  drivers/tty/serial/qcom_geni_serial.c | 85 ++++++++++++++++++++++++++++++++++-
>  1 file changed, 83 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> index e40d4a4..2ce83a57 100644
> --- a/drivers/tty/serial/qcom_geni_serial.c
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -196,8 +196,18 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
>  		timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
>  	}
>  
> -	return !readl_poll_timeout_atomic(uport->membase + offset, reg,
> -			 (bool)(reg & field) == set, 10, timeout_us);
> +	/*
> +	 * Use custom implementation instead of readl_poll_atomic since ktimer
> +	 * is not ready at the time of early console.
> +	 */
> +	while (timeout_us) {
> +		reg = readl_relaxed(uport->membase + offset);
> +		if ((bool)(reg & field) == set)
> +			return true;
> +		udelay(10);
> +		timeout_us -= 10;
> +	}
> +	return false;
>  }
>  
>  static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
> @@ -944,6 +954,77 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
>  	return uart_set_options(uport, co, baud, parity, bits, flow);
>  }
>  
> +static void qcom_geni_serial_earlycon_write(struct console *con,
> +					const char *s, unsigned int n)
> +{
> +	struct earlycon_device *dev = con->data;
> +
> +	__qcom_geni_serial_console_write(&dev->port, s, n);
> +}
> +
> +static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev,
> +								const char *opt)
> +{
> +	struct uart_port *uport = &dev->port;
> +	u32 tx_trans_cfg;
> +	u32 tx_parity_cfg = 0;	/* Disable Tx Parity */
> +	u32 rx_trans_cfg = 0;
> +	u32 rx_parity_cfg = 0;	/* Disable Rx Parity */
> +	u32 stop_bit_len = 0;	/* Default stop bit length - 1 bit */
> +	u32 bits_per_char;
> +	u32 ser_clk_cfg;
> +	u32 baud = 115200;

My understanding is that the baudrate should either be specified in
the earlycon parmameter or left as set up by the bootloader:

"If baud rate is not specified, the serial port must already be setup
and configured."

Documentation/admin-guide/kernel-parameters.txt

> +	unsigned int clk_div;
> +	unsigned long clk_rate;
> +	struct geni_se se;
> +
> +	if (!uport->membase)
> +		return -EINVAL;
> +
> +	memset(&se, 0, sizeof(se));
> +	se.base = uport->membase;
> +	if (geni_se_read_proto(&se) != GENI_SE_UART)
> +		return -ENXIO;

Declaring se on the stack and only initializing se.base is ugly, it
makes the assumption that geni_se_read_proto() does not use other
members of the struct, which is brittle.

Possible alternatives could be to duplicate the code of
geni_se_read_proto() here (also not ideal, though it's only a few
lines) or add a geni_se_read_proto_xyz(void __iomem *base) which is
then also called by geni_se_read_proto().

> +
> +	/*
> +	 * Ignore Flow control.
> +	 * n = 8.
> +	 */
> +	tx_trans_cfg = UART_CTS_MASK;
> +	bits_per_char = BITS_PER_BYTE;
> +
> +	clk_rate = get_clk_div_rate(baud, &clk_div);
> +	if (!clk_rate)
> +		return -EINVAL;

Here the parent clock rate and the corresponding divider are
calculated, however later only the divider is programmed, apparently
with the assumption that the calculated parent clock rate has been
configured by the bootloader.

I was told that at this stage the parent clock can't be
reconfigured. If we effectively rely on the bootloader to do part of
the initialization, shouldn't we then directly assume that the BL
initializes the hardware and only do the minimum to integrate it
with the kernel?

> +	ser_clk_cfg = SER_CLK_EN | (clk_div << CLK_DIV_SHFT);
> +	/*
> +	 * Make an unconditional cancel on the main sequencer to reset
> +	 * it else we could end up in data loss scenarios.
> +	 */
> +	qcom_geni_serial_poll_tx_done(uport);
> +	qcom_geni_serial_abort_rx(uport);
> +	geni_se_config_packing(&se, BITS_PER_BYTE, 1, false, true, false);
> +	geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2);
> +	geni_se_select_mode(&se, GENI_SE_FIFO);
> +
> +	writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
> +	writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
> +	writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
> +	writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
> +	writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
> +	writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
> +	writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
> +	writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
> +	writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
> +
> +	dev->con->write = qcom_geni_serial_earlycon_write;
> +	dev->con->setup = NULL;
> +	return 0;
> +}
> +OF_EARLYCON_DECLARE(qcom_serial, "qcom,geni-debug-uart",
> +				qcom_geni_serial_earlycon_setup);
> +
>  static int __init console_register(struct uart_driver *drv)
>  {
>  	return uart_register_driver(drv);
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux