serial: msm-geni: correct oversampling value based on QUP hardware revision

Starting from QUP v2.5 the value of oversampling is changed from 32
to 16, keeping the old value on newer platforms results on wrong set
UART IP clock divider, thus the asked baudrate does not correspond to
the actually set with all the consequencies for a user.

The change links the driver to a new Qualcomm GENI SE QUP driver
to get its hardware version and update the oversampling value.

Deliberately the code under CONFIG_DEBUG_UART_MSM_GENI is not touched,
since a wanted baudrate can be controlled by setting a modified
CONFIG_DEBUG_UART_CLOCK build time variable.

Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
This commit is contained in:
Vladimir Zapolskiy 2023-04-21 20:50:40 +03:00 committed by Tom Rini
parent aa539d89e1
commit 10ea2a7a93
2 changed files with 36 additions and 1 deletions

View file

@ -946,6 +946,8 @@ config MSM_SERIAL
config MSM_GENI_SERIAL config MSM_GENI_SERIAL
bool "Qualcomm on-chip GENI UART" bool "Qualcomm on-chip GENI UART"
select MISC
imply QCOM_GENI_SE
help help
Support UART based on Generic Interface (GENI) Serial Engine (SE), Support UART based on Generic Interface (GENI) Serial Engine (SE),
used on Qualcomm Snapdragon SoCs. Should support all qualcomm SOCs used on Qualcomm Snapdragon SoCs. Should support all qualcomm SOCs

View file

@ -13,6 +13,7 @@
#include <dm.h> #include <dm.h>
#include <errno.h> #include <errno.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <misc.h>
#include <serial.h> #include <serial.h>
#define UART_OVERSAMPLING 32 #define UART_OVERSAMPLING 32
@ -110,6 +111,10 @@
#define TX_FIFO_DEPTH_MSK (GENMASK(21, 16)) #define TX_FIFO_DEPTH_MSK (GENMASK(21, 16))
#define TX_FIFO_DEPTH_SHFT 16 #define TX_FIFO_DEPTH_SHFT 16
/* GENI SE QUP Registers */
#define QUP_HW_VER_REG 0x4
#define QUP_SE_VERSION_2_5 0x20050000
/* /*
* Predefined packing configuration of the serial engine (CFG0, CFG1 regs) * Predefined packing configuration of the serial engine (CFG0, CFG1 regs)
* for uart mode. * for uart mode.
@ -127,6 +132,7 @@ DECLARE_GLOBAL_DATA_PTR;
struct msm_serial_data { struct msm_serial_data {
phys_addr_t base; phys_addr_t base;
u32 baud; u32 baud;
u32 oversampling;
}; };
unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
@ -246,7 +252,7 @@ static int msm_serial_setbrg(struct udevice *dev, int baud)
priv->baud = baud; priv->baud = baud;
clk_rate = get_clk_div_rate(baud, UART_OVERSAMPLING, &clk_div); clk_rate = get_clk_div_rate(baud, priv->oversampling, &clk_div);
geni_serial_set_clock_rate(dev, clk_rate); geni_serial_set_clock_rate(dev, clk_rate);
geni_serial_baud(priv->base, clk_div, baud); geni_serial_baud(priv->base, clk_div, baud);
@ -480,6 +486,31 @@ static const struct dm_serial_ops msm_serial_ops = {
.setbrg = msm_serial_setbrg, .setbrg = msm_serial_setbrg,
}; };
static void geni_set_oversampling(struct udevice *dev)
{
struct msm_serial_data *priv = dev_get_priv(dev);
struct udevice *parent_dev = dev_get_parent(dev);
u32 geni_se_version;
int ret;
priv->oversampling = UART_OVERSAMPLING;
/*
* It could happen that GENI SE IP is missing in the board's device
* tree or GENI UART node is a direct child of SoC device tree node.
*/
if (device_get_uclass_id(parent_dev) != UCLASS_MISC)
return;
ret = misc_read(parent_dev, QUP_HW_VER_REG,
&geni_se_version, sizeof(geni_se_version));
if (ret != sizeof(geni_se_version))
return;
if (geni_se_version >= QUP_SE_VERSION_2_5)
priv->oversampling /= 2;
}
static inline void geni_serial_init(struct udevice *dev) static inline void geni_serial_init(struct udevice *dev)
{ {
struct msm_serial_data *priv = dev_get_priv(dev); struct msm_serial_data *priv = dev_get_priv(dev);
@ -523,6 +554,8 @@ static int msm_serial_probe(struct udevice *dev)
{ {
struct msm_serial_data *priv = dev_get_priv(dev); struct msm_serial_data *priv = dev_get_priv(dev);
geni_set_oversampling(dev);
/* No need to reinitialize the UART after relocation */ /* No need to reinitialize the UART after relocation */
if (gd->flags & GD_FLG_RELOC) if (gd->flags & GD_FLG_RELOC)
return 0; return 0;