Skip to content

Commit fc6a5b5

Browse files
vdadhanigregkh
authored andcommitted
serial: qcom-geni: Add DFS clock mode support to GENI UART driver
GENI UART driver currently supports only non-DFS (Dynamic Frequency Scaling) mode for source frequency selection. However, to operate correctly in DFS mode, the GENI SCLK register must be programmed with the appropriate DFS index. Failing to do so can result in incorrect frequency selection Add support for Dynamic Frequency Scaling (DFS) mode in the GENI UART driver by configuring the GENI_CLK_SEL register with the appropriate DFS index. This ensures correct frequency selection when operating in DFS mode. Replace the UART driver-specific logic for clock selection with the GENI common driver function to obtain the desired frequency and corresponding clock index. This improves maintainability and consistency across GENI-based drivers. Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com> Link: https://lore.kernel.org/r/20250903063136.3015237-1-viken.dadhaniya@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 5a66087 commit fc6a5b5

1 file changed

Lines changed: 21 additions & 71 deletions

File tree

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 21 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// SPDX-License-Identifier: GPL-2.0
2-
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
2+
/*
3+
* Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
4+
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5+
*/
36

47
/* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */
58
#define __DISABLE_TRACE_MMIO__
@@ -1242,93 +1245,38 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
12421245
return 0;
12431246
}
12441247

1245-
static unsigned long find_clk_rate_in_tol(struct clk *clk, unsigned int desired_clk,
1246-
unsigned int *clk_div, unsigned int percent_tol)
1247-
{
1248-
unsigned long freq;
1249-
unsigned long div, maxdiv;
1250-
u64 mult;
1251-
unsigned long offset, abs_tol, achieved;
1252-
1253-
abs_tol = div_u64((u64)desired_clk * percent_tol, 100);
1254-
maxdiv = CLK_DIV_MSK >> CLK_DIV_SHFT;
1255-
div = 1;
1256-
while (div <= maxdiv) {
1257-
mult = (u64)div * desired_clk;
1258-
if (mult != (unsigned long)mult)
1259-
break;
1260-
1261-
offset = div * abs_tol;
1262-
freq = clk_round_rate(clk, mult - offset);
1263-
1264-
/* Can only get lower if we're done */
1265-
if (freq < mult - offset)
1266-
break;
1267-
1268-
/*
1269-
* Re-calculate div in case rounding skipped rates but we
1270-
* ended up at a good one, then check for a match.
1271-
*/
1272-
div = DIV_ROUND_CLOSEST(freq, desired_clk);
1273-
achieved = DIV_ROUND_CLOSEST(freq, div);
1274-
if (achieved <= desired_clk + abs_tol &&
1275-
achieved >= desired_clk - abs_tol) {
1276-
*clk_div = div;
1277-
return freq;
1278-
}
1279-
1280-
div = DIV_ROUND_UP(freq, desired_clk);
1281-
}
1282-
1283-
return 0;
1284-
}
1285-
1286-
static unsigned long get_clk_div_rate(struct clk *clk, unsigned int baud,
1287-
unsigned int sampling_rate, unsigned int *clk_div)
1288-
{
1289-
unsigned long ser_clk;
1290-
unsigned long desired_clk;
1291-
1292-
desired_clk = baud * sampling_rate;
1293-
if (!desired_clk)
1294-
return 0;
1295-
1296-
/*
1297-
* try to find a clock rate within 2% tolerance, then within 5%
1298-
*/
1299-
ser_clk = find_clk_rate_in_tol(clk, desired_clk, clk_div, 2);
1300-
if (!ser_clk)
1301-
ser_clk = find_clk_rate_in_tol(clk, desired_clk, clk_div, 5);
1302-
1303-
return ser_clk;
1304-
}
1305-
13061248
static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud)
13071249
{
13081250
struct qcom_geni_serial_port *port = to_dev_port(uport);
13091251
unsigned long clk_rate;
1310-
unsigned int avg_bw_core;
1252+
unsigned int avg_bw_core, clk_idx;
13111253
unsigned int clk_div;
13121254
u32 ver, sampling_rate;
13131255
u32 ser_clk_cfg;
1256+
int ret;
13141257

13151258
sampling_rate = UART_OVERSAMPLING;
13161259
/* Sampling rate is halved for IP versions >= 2.5 */
13171260
ver = geni_se_get_qup_hw_version(&port->se);
13181261
if (ver >= QUP_SE_VERSION_2_5)
13191262
sampling_rate /= 2;
13201263

1321-
clk_rate = get_clk_div_rate(port->se.clk, baud,
1322-
sampling_rate, &clk_div);
1323-
if (!clk_rate) {
1324-
dev_err(port->se.dev,
1325-
"Couldn't find suitable clock rate for %u\n",
1326-
baud * sampling_rate);
1264+
ret = geni_se_clk_freq_match(&port->se, baud * sampling_rate, &clk_idx, &clk_rate, false);
1265+
if (ret) {
1266+
dev_err(port->se.dev, "Failed to find src clk for baud rate: %d ret: %d\n",
1267+
baud, ret);
1268+
return ret;
1269+
}
1270+
1271+
clk_div = DIV_ROUND_UP(clk_rate, baud * sampling_rate);
1272+
/* Check if calculated divider exceeds maximum allowed value */
1273+
if (clk_div > (CLK_DIV_MSK >> CLK_DIV_SHFT)) {
1274+
dev_err(port->se.dev, "Calculated clock divider %u exceeds maximum\n", clk_div);
13271275
return -EINVAL;
13281276
}
13291277

1330-
dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n",
1331-
baud * sampling_rate, clk_rate, clk_div);
1278+
dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n, clk_idx = %u\n",
1279+
baud * sampling_rate, clk_rate, clk_div, clk_idx);
13321280

13331281
uport->uartclk = clk_rate;
13341282
port->clk_rate = clk_rate;
@@ -1348,6 +1296,8 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud)
13481296

13491297
writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
13501298
writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
1299+
/* Configure clock selection register with the selected clock index */
1300+
writel(clk_idx & CLK_SEL_MSK, uport->membase + SE_GENI_CLK_SEL);
13511301
return 0;
13521302
}
13531303

0 commit comments

Comments
 (0)