Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Viken Dadhaniya and committed by
Greg Kroah-Hartman
fc6a5b54 5a660871

+21 -71
+21 -71
drivers/tty/serial/qcom_geni_serial.c
··· 1 1 // 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 + */ 3 6 4 7 /* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ 5 8 #define __DISABLE_TRACE_MMIO__ ··· 1245 1242 return 0; 1246 1243 } 1247 1244 1248 - static unsigned long find_clk_rate_in_tol(struct clk *clk, unsigned int desired_clk, 1249 - unsigned int *clk_div, unsigned int percent_tol) 1250 - { 1251 - unsigned long freq; 1252 - unsigned long div, maxdiv; 1253 - u64 mult; 1254 - unsigned long offset, abs_tol, achieved; 1255 - 1256 - abs_tol = div_u64((u64)desired_clk * percent_tol, 100); 1257 - maxdiv = CLK_DIV_MSK >> CLK_DIV_SHFT; 1258 - div = 1; 1259 - while (div <= maxdiv) { 1260 - mult = (u64)div * desired_clk; 1261 - if (mult != (unsigned long)mult) 1262 - break; 1263 - 1264 - offset = div * abs_tol; 1265 - freq = clk_round_rate(clk, mult - offset); 1266 - 1267 - /* Can only get lower if we're done */ 1268 - if (freq < mult - offset) 1269 - break; 1270 - 1271 - /* 1272 - * Re-calculate div in case rounding skipped rates but we 1273 - * ended up at a good one, then check for a match. 1274 - */ 1275 - div = DIV_ROUND_CLOSEST(freq, desired_clk); 1276 - achieved = DIV_ROUND_CLOSEST(freq, div); 1277 - if (achieved <= desired_clk + abs_tol && 1278 - achieved >= desired_clk - abs_tol) { 1279 - *clk_div = div; 1280 - return freq; 1281 - } 1282 - 1283 - div = DIV_ROUND_UP(freq, desired_clk); 1284 - } 1285 - 1286 - return 0; 1287 - } 1288 - 1289 - static unsigned long get_clk_div_rate(struct clk *clk, unsigned int baud, 1290 - unsigned int sampling_rate, unsigned int *clk_div) 1291 - { 1292 - unsigned long ser_clk; 1293 - unsigned long desired_clk; 1294 - 1295 - desired_clk = baud * sampling_rate; 1296 - if (!desired_clk) 1297 - return 0; 1298 - 1299 - /* 1300 - * try to find a clock rate within 2% tolerance, then within 5% 1301 - */ 1302 - ser_clk = find_clk_rate_in_tol(clk, desired_clk, clk_div, 2); 1303 - if (!ser_clk) 1304 - ser_clk = find_clk_rate_in_tol(clk, desired_clk, clk_div, 5); 1305 - 1306 - return ser_clk; 1307 - } 1308 - 1309 1245 static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) 1310 1246 { 1311 1247 struct qcom_geni_serial_port *port = to_dev_port(uport); 1312 1248 unsigned long clk_rate; 1313 - unsigned int avg_bw_core; 1249 + unsigned int avg_bw_core, clk_idx; 1314 1250 unsigned int clk_div; 1315 1251 u32 ver, sampling_rate; 1316 1252 u32 ser_clk_cfg; 1253 + int ret; 1317 1254 1318 1255 sampling_rate = UART_OVERSAMPLING; 1319 1256 /* Sampling rate is halved for IP versions >= 2.5 */ ··· 1261 1318 if (ver >= QUP_SE_VERSION_2_5) 1262 1319 sampling_rate /= 2; 1263 1320 1264 - clk_rate = get_clk_div_rate(port->se.clk, baud, 1265 - sampling_rate, &clk_div); 1266 - if (!clk_rate) { 1267 - dev_err(port->se.dev, 1268 - "Couldn't find suitable clock rate for %u\n", 1269 - baud * sampling_rate); 1321 + ret = geni_se_clk_freq_match(&port->se, baud * sampling_rate, &clk_idx, &clk_rate, false); 1322 + if (ret) { 1323 + dev_err(port->se.dev, "Failed to find src clk for baud rate: %d ret: %d\n", 1324 + baud, ret); 1325 + return ret; 1326 + } 1327 + 1328 + clk_div = DIV_ROUND_UP(clk_rate, baud * sampling_rate); 1329 + /* Check if calculated divider exceeds maximum allowed value */ 1330 + if (clk_div > (CLK_DIV_MSK >> CLK_DIV_SHFT)) { 1331 + dev_err(port->se.dev, "Calculated clock divider %u exceeds maximum\n", clk_div); 1270 1332 return -EINVAL; 1271 1333 } 1272 1334 1273 - dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n", 1274 - baud * sampling_rate, clk_rate, clk_div); 1335 + dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n, clk_idx = %u\n", 1336 + baud * sampling_rate, clk_rate, clk_div, clk_idx); 1275 1337 1276 1338 uport->uartclk = clk_rate; 1277 1339 port->clk_rate = clk_rate; ··· 1296 1348 1297 1349 writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG); 1298 1350 writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG); 1351 + /* Configure clock selection register with the selected clock index */ 1352 + writel(clk_idx & CLK_SEL_MSK, uport->membase + SE_GENI_CLK_SEL); 1299 1353 return 0; 1300 1354 } 1301 1355