diff options
Diffstat (limited to 'drivers/serial/serial_msm.c')
| -rw-r--r-- | drivers/serial/serial_msm.c | 109 | 
1 files changed, 84 insertions, 25 deletions
| diff --git a/drivers/serial/serial_msm.c b/drivers/serial/serial_msm.c index ac4280c6c4c..a472e0b3683 100644 --- a/drivers/serial/serial_msm.c +++ b/drivers/serial/serial_msm.c @@ -32,6 +32,16 @@  #define UARTDM_RXFS_BUF_MASK    0x7  #define UARTDM_MR1				 0x00  #define UARTDM_MR2				 0x04 +/* + * This is documented on page 1817 of the apq8016e technical reference manual. + * section 6.2.5.3.26 + * + * The upper nybble contains the bit clock divider for the RX pin, the lower + * nybble defines the TX pin. In almost all cases these should be the same value. + * + * The baud rate is the core clock frequency divided by the fixed divider value + * programmed into this register (defined in calc_csr_bitrate()). + */  #define UARTDM_CSR				 0xA0  #define UARTDM_SR                0xA4 /* Status register */ @@ -53,10 +63,10 @@  #define UARTDM_TF               0x100 /* UART Transmit FIFO register */  #define UARTDM_RF               0x140 /* UART Receive FIFO register */ -#define UART_DM_CLK_RX_TX_BIT_RATE 0xCC -#define MSM_BOOT_UART_DM_8_N_1_MODE 0x34 -#define MSM_BOOT_UART_DM_CMD_RESET_RX 0x10 -#define MSM_BOOT_UART_DM_CMD_RESET_TX 0x20 +#define MSM_BOOT_UART_DM_8_N_1_MODE	0x34 +#define MSM_BOOT_UART_DM_CMD_RESET_RX	0x10 +#define MSM_BOOT_UART_DM_CMD_RESET_TX	0x20 +#define MSM_UART_MR1_RX_RDY_CTL		BIT(7)  DECLARE_GLOBAL_DATA_PTR; @@ -64,7 +74,7 @@ struct msm_serial_data {  	phys_addr_t base;  	unsigned chars_cnt; /* number of buffered chars */  	uint32_t chars_buf; /* buffered chars */ -	uint32_t clk_bit_rate; /* data mover mode bit rate register value */ +	uint32_t clk_rate; /* core clock rate */  };  static int msm_serial_fetch(struct udevice *dev) @@ -156,33 +166,63 @@ static const struct dm_serial_ops msm_serial_ops = {  	.getc = msm_serial_getc,  }; -static int msm_uart_clk_init(struct udevice *dev) +static long msm_uart_clk_init(struct udevice *dev)  { -	uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), -					"clock-frequency", 115200); +	struct msm_serial_data *priv = dev_get_priv(dev);  	struct clk clk;  	int ret; +	long rate;  	ret = clk_get_by_name(dev, "core", &clk);  	if (ret < 0) {  		pr_warn("%s: Failed to get clock: %d\n", __func__, ret); -		return ret; +		return 0;  	} -	ret = clk_set_rate(&clk, clk_rate); -	if (ret < 0) -		return ret; +	rate = clk_set_rate(&clk, priv->clk_rate); -	return 0; +	return rate; +} + +static int calc_csr_bitrate(struct msm_serial_data *priv) +{ +	/* This table is from the TRE. See the definition of UARTDM_CSR */ +	unsigned int csr_div_table[] = {24576, 12288, 6144, 3072, 1536, 768, 512, 384, +					256,   192,   128,  96,   64,   48,  32,  16}; +	int i = ARRAY_SIZE(csr_div_table) - 1; +	/* Currently we only support one baudrate */ +	int baud = 115200; + +	for (; i >= 0; i--) { +		int x = priv->clk_rate / csr_div_table[i]; + +		if (x == baud) +			/* Duplicate the configuration for RX +			 * as the lower nybble only configures TX +			 */ +			return i + (i << 4); +	} + +	return -EINVAL;  }  static void uart_dm_init(struct msm_serial_data *priv)  {  	/* Delay initialization for a bit to let pins stabilize if necessary */  	mdelay(5); +	int bitrate = calc_csr_bitrate(priv); +	if (bitrate < 0) { +		log_warning("Couldn't calculate bit clock divider! Using default\n"); +		/* This happens to be the value used on MSM8916 for the hardcoded clockrate +		 * in clock-apq8016. It's at least a better guess than a value we *know* +		 * is wrong... +		 */ +		bitrate = 0xCC; +	} -	writel(priv->clk_bit_rate, priv->base + UARTDM_CSR); -	writel(0x0, priv->base + UARTDM_MR1); +	writel(bitrate, priv->base + UARTDM_CSR); +	/* Enable RS232 flow control to support RS232 db9 connector */ +	writel(MSM_UART_MR1_RX_RDY_CTL, priv->base + UARTDM_MR1);  	writel(MSM_BOOT_UART_DM_8_N_1_MODE, priv->base + UARTDM_MR2);  	writel(MSM_BOOT_UART_DM_CMD_RESET_RX, priv->base + UARTDM_CR);  	writel(MSM_BOOT_UART_DM_CMD_RESET_TX, priv->base + UARTDM_CR); @@ -192,16 +232,25 @@ static void uart_dm_init(struct msm_serial_data *priv)  }  static int msm_serial_probe(struct udevice *dev)  { -	int ret;  	struct msm_serial_data *priv = dev_get_priv(dev); +	long rate;  	/* No need to reinitialize the UART after relocation */  	if (gd->flags & GD_FLG_RELOC)  		return 0; -	ret = msm_uart_clk_init(dev); -	if (ret) -		return ret; +	rate = msm_uart_clk_init(dev); +	if (rate < 0) +		return rate; +	if (!rate) { +		log_err("Got core clock rate of 0... Please fix your clock driver\n"); +		return -EINVAL; +	} + +	/* Update the clock rate to the actual programmed rate returned by the +	 * clock driver +	 */ +	priv->clk_rate = rate;  	uart_dm_init(priv); @@ -211,13 +260,18 @@ static int msm_serial_probe(struct udevice *dev)  static int msm_serial_of_to_plat(struct udevice *dev)  {  	struct msm_serial_data *priv = dev_get_priv(dev); +	int ret;  	priv->base = dev_read_addr(dev);  	if (priv->base == FDT_ADDR_T_NONE)  		return -EINVAL; -	priv->clk_bit_rate = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), -							"bit-rate", UART_DM_CLK_RX_TX_BIT_RATE); +	ret = dev_read_u32(dev, "clock-frequency", &priv->clk_rate); +	if (ret < 0) { +		log_debug("No clock frequency specified, using default rate\n"); +		/* Default for APQ8016 */ +		priv->clk_rate = 7372800; +	}  	return 0;  } @@ -242,18 +296,23 @@ U_BOOT_DRIVER(serial_msm) = {  static struct msm_serial_data init_serial_data = {  	.base = CONFIG_VAL(DEBUG_UART_BASE), -	.clk_rate = 7372800, +	.clk_rate = CONFIG_VAL(DEBUG_UART_CLOCK),  };  #include <debug_uart.h>  /* Uncomment to turn on UART clocks when debugging U-Boot as aboot on MSM8916 */ -//int apq8016_clk_init_uart(phys_addr_t gcc_base); +//int apq8016_clk_init_uart(phys_addr_t gcc_base, unsigned long id);  static inline void _debug_uart_init(void)  { -	/* Uncomment to turn on UART clocks when debugging U-Boot as aboot on MSM8916 */ -	//apq8016_clk_init_uart(0x1800000); +	/* +	 * Uncomment to turn on UART clocks when debugging U-Boot as aboot +	 * on MSM8916. Supported debug UART clock IDs: +	 *   - db410c: GCC_BLSP1_UART2_APPS_CLK +	 *   - HMIBSC: GCC_BLSP1_UART1_APPS_CLK +	 */ +	//apq8016_clk_init_uart(0x1800000, <uart_clk_id>);  	uart_dm_init(&init_serial_data);  } | 
