* [PATCH v3 1/8] soc: qcom: geni-se: Populate clk_perf_tbl with SE source clock frequencies from perf OPP table
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-22 12:22 ` Mukesh Savaliya
2026-10-15 12:58 ` [PATCH v3 2/8] soc: qcom: geni-se: Add helper to set SE clock rate via OPP Praveen Talari
` (6 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
Currently, on the SA8255P platform, protocol drivers attached via
geni_se_domain_attach() treat each OPP on the perf domain as directly
corresponding to a protocol value such as a baudrate or requested
frequency, and simply request that OPP via
geni_se_set_perf_level()/geni_se_set_perf_opp(). This does not allow
computing a source clock and divider combination for a protocol
requested frequency, unlike the Linux clock managed path which derives
this from se->clk_perf_tbl via geni_se_clk_freq_match(), and then
applies the matched source clock frequency with dev_pm_opp_set_rate().
Change this by treating the OPP table exposed on the perf domain
device as representing the actual SE HW supported source clock
frequencies, the same role clk_perf_tbl plays for the Linux clock
managed path. Populate se->clk_perf_tbl and se->num_clk_levels by
iterating over this OPP table in geni_se_domain_attach(), so that
protocol drivers on the firmware managed (SA8255P) path can also use
geni_se_clk_freq_match() to pick the closest supported source clock
frequency and calculate the required divider, and apply it with
dev_pm_opp_set_rate() the same way as it is done for the Linux clock
managed path, instead of relying on a direct frequency/baudrate-to-
perf-level mapping.
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/soc/qcom/qcom-geni-se.c | 34 +++++++++++++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 074087c592cc..95eeb9438a29 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -1159,7 +1159,8 @@ EXPORT_SYMBOL_GPL(geni_se_set_perf_opp);
*
* This function attaches the power domains ("power" and "perf") required
* in the SCMI auto-VM environment to the GENI Serial Engine device. It
- * initializes se->pd_list with the attached domains.
+ * initializes se->pd_list with the attached domains, and populates
+ * se->clk_perf_tbl from the OPP table of the "perf" domain device.
*
* Return: 0 on success, or a negative error code on failure.
*/
@@ -1170,7 +1171,12 @@ int geni_se_domain_attach(struct geni_se *se)
.pd_names = (const char*[]) { "power", "perf" },
.num_pd_names = 2,
};
+ struct device *perf_dev;
+ struct dev_pm_opp *opp;
+ unsigned int level;
+ int num_opps;
int ret;
+ int i;
ret = devm_pm_domain_attach_list(se->dev,
&pd_data, &se->pd_list);
@@ -1179,6 +1185,32 @@ int geni_se_domain_attach(struct geni_se *se)
else if (ret < 0)
return ret;
+ perf_dev = se->pd_list->pd_devs[DOMAIN_IDX_PERF];
+
+ num_opps = dev_pm_opp_get_opp_count(perf_dev);
+ if (num_opps < 0)
+ return num_opps;
+ if (num_opps == 0)
+ return -ENODEV;
+
+ se->clk_perf_tbl = devm_kcalloc(se->dev, num_opps,
+ sizeof(*se->clk_perf_tbl),
+ GFP_KERNEL);
+ if (!se->clk_perf_tbl)
+ return -ENOMEM;
+
+ level = 0;
+ for (i = 0; i < num_opps; i++) {
+ opp = dev_pm_opp_find_level_ceil(perf_dev, &level);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ se->clk_perf_tbl[i] = level;
+ dev_pm_opp_put(opp);
+ level++;
+ }
+ se->num_clk_levels = num_opps;
+
return 0;
}
EXPORT_SYMBOL_GPL(geni_se_domain_attach);
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v3 1/8] soc: qcom: geni-se: Populate clk_perf_tbl with SE source clock frequencies from perf OPP table
2026-10-15 12:58 ` [PATCH v3 1/8] soc: qcom: geni-se: Populate clk_perf_tbl with SE source clock frequencies from perf OPP table Praveen Talari
@ 2026-10-22 12:22 ` Mukesh Savaliya
0 siblings, 0 replies; 11+ messages in thread
From: Mukesh Savaliya @ 2026-10-22 12:22 UTC (permalink / raw)
To: Praveen Talari, konrad.dybcio, Sudeep Holla, Cristian Marussi,
Ulf Hansson, Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman,
Jiri Slaby, Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: chandana.chiluveru, arm-scmi, linux-arm-kernel, linux-pm,
linux-kernel, linux-arm-msm, linux-serial, linux-spi, linux-i2c
On 10/15/2026 6:28 PM, Praveen Talari wrote:
> Currently, on the SA8255P platform, protocol drivers attached via
> geni_se_domain_attach() treat each OPP on the perf domain as directly
> corresponding to a protocol value such as a baudrate or requested
> frequency, and simply request that OPP via
> geni_se_set_perf_level()/geni_se_set_perf_opp(). This does not allow
> computing a source clock and divider combination for a protocol
> requested frequency, unlike the Linux clock managed path which derives
> this from se->clk_perf_tbl via geni_se_clk_freq_match(), and then
> applies the matched source clock frequency with dev_pm_opp_set_rate().
>
> Change this by treating the OPP table exposed on the perf domain
> device as representing the actual SE HW supported source clock
> frequencies, the same role clk_perf_tbl plays for the Linux clock
> managed path. Populate se->clk_perf_tbl and se->num_clk_levels by
> iterating over this OPP table in geni_se_domain_attach(), so that
> protocol drivers on the firmware managed (SA8255P) path can also use
> geni_se_clk_freq_match() to pick the closest supported source clock
> frequency and calculate the required divider, and apply it with
> dev_pm_opp_set_rate() the same way as it is done for the Linux clock
> managed path, instead of relying on a direct frequency/baudrate-to-
> perf-level mapping.
>
> Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
> ---
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 2/8] soc: qcom: geni-se: Add helper to set SE clock rate via OPP
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
2026-10-15 12:58 ` [PATCH v3 1/8] soc: qcom: geni-se: Populate clk_perf_tbl with SE source clock frequencies from perf OPP table Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-15 12:58 ` [PATCH v3 3/8] soc: qcom: geni-se: Remove OPP rate reset from resource deactivation Praveen Talari
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
GENI protocol drivers need a common way to scale the SE source clock
through the OPP framework. However, the device that owns the OPP table
differs depending on how the SE resources are managed. For Linux clock
managed platforms, the OPP table is associated with the SE device,
whereas on firmware-managed platforms it is associated with the
performance power-domain device. This requires protocol drivers to be
aware of the underlying resource management model when requesting
frequency changes.
Introduce geni_se_set_rate(), a common helper that abstracts this
difference and applies the requested frequency through the appropriate
device. The helper automatically selects the performance-domain device
when power domains are attached and falls back to the SE device
otherwise.
Acked-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/soc/qcom/qcom-geni-se.c | 22 ++++++++++++++++++++++
include/linux/soc/qcom/geni-se.h | 2 ++
2 files changed, 24 insertions(+)
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 95eeb9438a29..97905bd6d363 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -1153,6 +1153,28 @@ int geni_se_set_perf_opp(struct geni_se *se, unsigned long clk_freq)
}
EXPORT_SYMBOL_GPL(geni_se_set_perf_opp);
+/**
+ * geni_se_set_rate() - Set the SE source clock rate via the OPP framework.
+ * @se: Pointer to the struct geni_se instance.
+ * @freq: The source clock frequency to set.
+ *
+ * Applies the given frequency through dev_pm_opp_set_rate(), targeting the
+ * perf domain device when the SE has power domains attached (firmware
+ * managed path), or se->dev otherwise (Linux clock managed path).
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_set_rate(struct geni_se *se, unsigned long freq)
+{
+ struct device *perf_dev = se->dev;
+
+ if (se->pd_list)
+ perf_dev = se->pd_list->pd_devs[DOMAIN_IDX_PERF];
+
+ return dev_pm_opp_set_rate(perf_dev, freq);
+}
+EXPORT_SYMBOL_GPL(geni_se_set_rate);
+
/**
* geni_se_domain_attach() - Attach power domains to a GENI SE device.
* @se: Pointer to the geni_se structure representing the GENI SE device.
diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
index b3372a3147a0..8c3c5b68151b 100644
--- a/include/linux/soc/qcom/geni-se.h
+++ b/include/linux/soc/qcom/geni-se.h
@@ -597,5 +597,7 @@ int geni_se_domain_attach(struct geni_se *se);
int geni_se_set_perf_level(struct geni_se *se, unsigned long level);
int geni_se_set_perf_opp(struct geni_se *se, unsigned long clk_freq);
+
+int geni_se_set_rate(struct geni_se *se, unsigned long freq);
#endif
#endif
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 3/8] soc: qcom: geni-se: Remove OPP rate reset from resource deactivation
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
2026-10-15 12:58 ` [PATCH v3 1/8] soc: qcom: geni-se: Populate clk_perf_tbl with SE source clock frequencies from perf OPP table Praveen Talari
2026-10-15 12:58 ` [PATCH v3 2/8] soc: qcom: geni-se: Add helper to set SE clock rate via OPP Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-15 12:58 ` [PATCH v3 4/8] serial: qcom-geni: Use geni_se_set_rate() for source clock configuration Praveen Talari
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
Remove the dev_pm_opp_set_rate(se->dev, 0) call from
geni_se_resources_deactivate().
OPP resource management should be controlled by the consumer driver.
Additionally, clocks are already disabled by geni_se_clks_off(), making
the OPP rate reset unnecessary during resource deactivation.
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/soc/qcom/qcom-geni-se.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 97905bd6d363..6163c19c6cf8 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -1043,8 +1043,8 @@ EXPORT_SYMBOL_GPL(geni_icc_disable);
* geni_se_resources_deactivate() - Deactivate GENI SE device resources
* @se: Pointer to the geni_se structure
*
- * Deactivates device resources for power saving: OPP rate to 0, pin control
- * to sleep state, turns off clocks, and disables interconnect. Skips ACPI devices.
+ * Deactivates device resources for power saving: pin control to sleep
+ * state, turns off clocks, and disables interconnect. Skips ACPI devices.
*
* Return: 0 on success, negative error code on failure
*/
@@ -1055,9 +1055,6 @@ int geni_se_resources_deactivate(struct geni_se *se)
if (has_acpi_companion(se->dev))
return 0;
- if (se->has_opp)
- dev_pm_opp_set_rate(se->dev, 0);
-
ret = pinctrl_pm_select_sleep_state(se->dev);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 4/8] serial: qcom-geni: Use geni_se_set_rate() for source clock configuration
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
` (2 preceding siblings ...)
2026-10-15 12:58 ` [PATCH v3 3/8] soc: qcom: geni-se: Remove OPP rate reset from resource deactivation Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-15 12:58 ` [PATCH v3 5/8] spi: qcom-geni: Use geni_se_set_rate() for setting source clock frequency Praveen Talari
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
On the SA8255P platform there is no Linux clock handler for the SE
source clock, so the driver has no way to compute the divider needed
for a requested baud rate. To work around this, firmware instead
exposes the SE HW supported source clock frequencies as OPPs on the
perf domain device.
geni_se_domain_attach() now populates clk_perf_tbl from
the performance-domain OPP table, allowing firmware-managed
platforms to use geni_se_clk_freq_match().
Call geni_serial_set_rate() unconditionally from
qcom_geni_serial_set_termios(), and use geni_se_set_rate() instead of
dev_pm_opp_set_rate() so clock programming is handled centrally by
the GENI core, independent of the underlying clock-control mechanism.
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/tty/serial/qcom_geni_serial.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 4a6f526340b8..23e423009255 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -113,7 +113,6 @@ struct qcom_geni_device_data {
bool console;
enum geni_se_xfer_mode mode;
int (*resources_init)(struct geni_se *se);
- int (*set_rate)(struct geni_se *se, unsigned long baud);
int (*power_on)(struct geni_se *se);
int (*power_off)(struct geni_se *se);
};
@@ -1461,7 +1460,10 @@ static int geni_serial_set_rate(struct geni_se *se, unsigned long baud)
uport->uartclk = clk_rate;
port->clk_rate = clk_rate;
- dev_pm_opp_set_rate(uport->dev, clk_rate);
+ ret = geni_se_set_rate(&port->se, clk_rate);
+ if (ret)
+ return ret;
+
ser_clk_cfg = SER_CLK_EN;
ser_clk_cfg |= clk_div << CLK_DIV_SHFT;
@@ -1499,7 +1501,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 8000000);
- ret = port->dev_data->set_rate(&port->se, baud);
+ ret = geni_serial_set_rate(&port->se, baud);
if (ret)
return;
@@ -2051,15 +2053,19 @@ static void qcom_geni_serial_remove(struct platform_device *pdev)
static int __maybe_unused qcom_geni_serial_runtime_suspend(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
+ int ret;
- return port->dev_data->power_off ?
- port->dev_data->power_off(&port->se) : 0;
+ ret = port->dev_data->power_off ?
+ port->dev_data->power_off(&port->se) : 0;
+ if (ret)
+ return ret;
+
+ return geni_se_set_rate(&port->se, 0);
}
static int __maybe_unused qcom_geni_serial_runtime_resume(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
- struct uart_port *uport = &port->uport;
int ret;
if (port->dev_data->power_on) {
@@ -2068,8 +2074,8 @@ static int __maybe_unused qcom_geni_serial_runtime_resume(struct device *dev)
return ret;
}
- if (port->se.has_opp && port->clk_rate)
- return dev_pm_opp_set_rate(uport->dev, port->clk_rate);
+ if (port->clk_rate)
+ return geni_se_set_rate(&port->se, port->clk_rate);
return 0;
}
@@ -2130,7 +2136,6 @@ static const struct qcom_geni_device_data qcom_geni_console_data = {
.console = true,
.mode = GENI_SE_FIFO,
.resources_init = geni_se_resources_init,
- .set_rate = geni_serial_set_rate,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
};
@@ -2139,7 +2144,6 @@ static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
.console = true,
.mode = GENI_SE_FIFO,
.resources_init = geni_se_domain_attach,
- .set_rate = geni_se_set_perf_level,
};
#endif
@@ -2147,7 +2151,6 @@ static const struct qcom_geni_device_data qcom_geni_uart_data = {
.console = false,
.mode = GENI_SE_DMA,
.resources_init = geni_se_resources_init,
- .set_rate = geni_serial_set_rate,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
};
@@ -2156,7 +2159,6 @@ static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = {
.console = false,
.mode = GENI_SE_DMA,
.resources_init = geni_se_domain_attach,
- .set_rate = geni_se_set_perf_level,
};
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 5/8] spi: qcom-geni: Use geni_se_set_rate() for setting source clock frequency
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
` (3 preceding siblings ...)
2026-10-15 12:58 ` [PATCH v3 4/8] serial: qcom-geni: Use geni_se_set_rate() for source clock configuration Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-15 12:58 ` [PATCH v3 6/8] i2c: qcom-geni: Vote for SE clock rate using OPP Praveen Talari
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
On the SA8255P platform there is no Linux clock handler for the SE
source clock, so the driver has no way to look up the source clock
index and divider needed to fill the GSI CONFIG0 TRE for a requested
transfer frequency. To work around this, firmware instead exposes the
SE HW supported source clock frequencies as OPPs on the perf domain
device.
geni_se_domain_attach() now populates clk_perf_tbl from
the performance-domain OPP table, allowing firmware-managed
platforms to use geni_se_clk_freq_match().
Use geni_se_set_rate() instead of dev_pm_opp_set_rate()
so clock programming is handled centrally by the GENI core,
independent of the underlying clock-control mechanism.
Acked-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/spi/spi-geni-qcom.c | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 91e75e213f92..cbccd6d8689a 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -18,7 +18,6 @@
#include <linux/module.h>
#include <linux/panic_notifier.h>
#include <linux/platform_device.h>
-#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/soc/qcom/geni-se.h>
@@ -85,7 +84,6 @@
struct geni_spi_desc {
int (*resources_init)(struct geni_se *se);
- int (*set_rate)(struct geni_se *se, unsigned long clk_freq);
int (*power_on)(struct geni_se *se);
int (*power_off)(struct geni_se *se);
};
@@ -155,9 +153,9 @@ static int get_spi_clk_cfg(unsigned int speed_hz,
dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz,
actual_hz, sclk_freq, *clk_idx, *clk_div);
- ret = dev_pm_opp_set_rate(mas->dev, sclk_freq);
+ ret = geni_se_set_rate(&mas->se, sclk_freq);
if (ret)
- dev_err(mas->dev, "dev_pm_opp_set_rate failed %d\n", ret);
+ dev_err(mas->dev, "geni_se_set_rate failed %d\n", ret);
else
mas->cur_sclk_hz = sclk_freq;
@@ -850,7 +848,7 @@ static int setup_se_xfer(struct spi_transfer *xfer,
}
/* Speed and bits per word can be overridden per transfer */
- ret = mas->dev_data->set_rate(&mas->se, xfer->speed_hz);
+ ret = geni_spi_set_clock_and_bw(&mas->se, xfer->speed_hz);
if (ret)
return ret;
@@ -1248,9 +1246,14 @@ static int spi_geni_runtime_suspend(struct device *dev)
{
struct spi_controller *spi = dev_get_drvdata(dev);
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
+ int ret;
+
+ ret = mas->dev_data->power_off ?
+ mas->dev_data->power_off(&mas->se) : 0;
+ if (ret)
+ return ret;
- return mas->dev_data->power_off ?
- mas->dev_data->power_off(&mas->se) : 0;
+ return geni_se_set_rate(&mas->se, 0);
}
static int spi_geni_runtime_resume(struct device *dev)
@@ -1265,10 +1268,7 @@ static int spi_geni_runtime_resume(struct device *dev)
return ret;
}
- if (mas->se.has_opp)
- return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
-
- return 0;
+ return geni_se_set_rate(&mas->se, mas->cur_sclk_hz);
}
static int spi_geni_suspend(struct device *dev)
@@ -1310,14 +1310,12 @@ static const struct dev_pm_ops spi_geni_pm_ops = {
static const struct geni_spi_desc geni_spi = {
.resources_init = geni_se_resources_init,
- .set_rate = geni_spi_set_clock_and_bw,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
};
static const struct geni_spi_desc sa8255p_geni_spi = {
.resources_init = geni_se_domain_attach,
- .set_rate = geni_se_set_perf_opp,
};
static const struct of_device_id spi_geni_dt_match[] = {
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 6/8] i2c: qcom-geni: Vote for SE clock rate using OPP
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
` (4 preceding siblings ...)
2026-10-15 12:58 ` [PATCH v3 5/8] spi: qcom-geni: Use geni_se_set_rate() for setting source clock frequency Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-22 12:28 ` Mukesh Savaliya
2026-10-15 12:58 ` [PATCH v3 7/8] i2c: qcom-geni: Use common GENI resource initialization helper Praveen Talari
2026-10-15 12:58 ` [PATCH v3 8/8] i2c: qcom-geni: Use geni_se_set_rate() for setting source clock frequency Praveen Talari
7 siblings, 1 reply; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
The GENI I2C driver selects a source clock based on the requested bus
frequency and programs the corresponding clock selector. However, it
does not request the selected source clock rate when OPP-based clock
scaling is used.
Store the matched source clock frequency and vote for it through
dev_pm_opp_set_rate() before programming the clock selector. Also remove
and restore the vote during runtime suspend and resume.
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 5f3d260a3913..a6fe89d0934f 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/soc/qcom/geni-se.h>
#include <linux/spinlock.h>
@@ -136,6 +137,7 @@ struct geni_i2c_dev {
u32 clk_freq_out;
const struct geni_i2c_clk_fld *clk_fld;
u32 clk_idx;
+ unsigned long src_freq;
void *dma_buf;
size_t xfer_len;
dma_addr_t dma_addr;
@@ -206,7 +208,7 @@ static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
{
const struct geni_i2c_clk_fld *itr;
- unsigned long req_freq, res_freq;
+ unsigned long req_freq;
int ret;
/*
@@ -223,14 +225,14 @@ static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
}
ret = geni_se_clk_freq_match(&gi2c->se, req_freq, &gi2c->clk_idx,
- &res_freq, true);
+ &gi2c->src_freq, true);
if (ret)
return dev_err_probe(gi2c->se.dev, ret,
"Failed to find SE source clock %lu Hz\n",
req_freq);
- if (res_freq != req_freq) {
+ if (gi2c->src_freq != req_freq) {
dev_err(gi2c->se.dev, "Unsupported SE source clock %lu Hz\n",
- res_freq);
+ gi2c->src_freq);
return -EINVAL;
}
@@ -255,6 +257,11 @@ static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq)
struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev);
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
u32 val;
+ int ret;
+
+ ret = dev_pm_opp_set_rate(se->dev, gi2c->src_freq);
+ if (ret)
+ return ret;
writel_relaxed(gi2c->clk_idx, gi2c->se.base + SE_GENI_CLK_SEL);
@@ -1286,7 +1293,7 @@ static int geni_i2c_runtime_suspend(struct device *dev)
}
}
- return 0;
+ return dev_pm_opp_set_rate(dev, 0);
}
static int geni_i2c_runtime_resume(struct device *dev)
@@ -1300,6 +1307,12 @@ static int geni_i2c_runtime_resume(struct device *dev)
return ret;
}
+ if (gi2c->src_freq) {
+ ret = dev_pm_opp_set_rate(dev, gi2c->src_freq);
+ if (ret)
+ return ret;
+ }
+
enable_irq(gi2c->irq);
return 0;
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v3 6/8] i2c: qcom-geni: Vote for SE clock rate using OPP
2026-10-15 12:58 ` [PATCH v3 6/8] i2c: qcom-geni: Vote for SE clock rate using OPP Praveen Talari
@ 2026-10-22 12:28 ` Mukesh Savaliya
0 siblings, 0 replies; 11+ messages in thread
From: Mukesh Savaliya @ 2026-10-22 12:28 UTC (permalink / raw)
To: Praveen Talari, konrad.dybcio, Sudeep Holla, Cristian Marussi,
Ulf Hansson, Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman,
Jiri Slaby, Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: chandana.chiluveru, arm-scmi, linux-arm-kernel, linux-pm,
linux-kernel, linux-arm-msm, linux-serial, linux-spi, linux-i2c
On 10/15/2026 6:28 PM, Praveen Talari wrote:
> The GENI I2C driver selects a source clock based on the requested bus
> frequency and programs the corresponding clock selector. However, it
> does not request the selected source clock rate when OPP-based clock
> scaling is used.
>
> Store the matched source clock frequency and vote for it through
> dev_pm_opp_set_rate() before programming the clock selector. Also remove
> and restore the vote during runtime suspend and resume.
>
> Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
> ---
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 7/8] i2c: qcom-geni: Use common GENI resource initialization helper
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
` (5 preceding siblings ...)
2026-10-15 12:58 ` [PATCH v3 6/8] i2c: qcom-geni: Vote for SE clock rate using OPP Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
2026-10-15 12:58 ` [PATCH v3 8/8] i2c: qcom-geni: Use geni_se_set_rate() for setting source clock frequency Praveen Talari
7 siblings, 0 replies; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
The driver implements a custom resources_init() callback for clock
frequency validation and bandwidth vote programming. Neither operation is
required for resource initialization itself.
Move clock frequency validation to probe and program the ICC bandwidth vote
from qcom_geni_i2c_conf(), where the bus frequency is configured. This
allows the driver to use geni_se_resources_init() directly and removes the
I2C-specific resource initialization wrapper.
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 29 ++++++++++-------------------
1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index a6fe89d0934f..dbe344719b86 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -275,7 +275,9 @@ static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq)
trace_geni_i2c_bus_setup(gi2c->se.dev, gi2c->clk_freq_out,
itr->clk_div, itr->t_high_cnt,
itr->t_low_cnt, itr->t_cycle_cnt);
- return 0;
+
+ return geni_icc_set_bw_ab(&gi2c->se, GENI_DEFAULT_BW, GENI_DEFAULT_BW,
+ Bps_to_icc(gi2c->clk_freq_out));
}
static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
@@ -1167,22 +1169,6 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c)
return ret;
}
-static int geni_i2c_resources_init(struct geni_se *se)
-{
- struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev);
- int ret;
-
- ret = geni_se_resources_init(&gi2c->se);
- if (ret)
- return ret;
-
- ret = geni_i2c_clk_map_idx(gi2c);
- if (ret)
- return ret;
-
- return geni_icc_set_bw_ab(&gi2c->se, GENI_DEFAULT_BW, GENI_DEFAULT_BW,
- Bps_to_icc(gi2c->clk_freq_out));
-}
static int geni_i2c_probe(struct platform_device *pdev)
{
@@ -1251,6 +1237,11 @@ static int geni_i2c_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ ret = geni_i2c_clk_map_idx(gi2c);
+ if (ret)
+ return dev_err_probe(gi2c->se.dev, ret, "Invalid clk frequency %d Hz\n",
+ gi2c->clk_freq_out);
+
ret = i2c_add_adapter(&gi2c->adap);
if (ret) {
release_gpi_dma(gi2c);
@@ -1353,7 +1344,7 @@ static const struct dev_pm_ops geni_i2c_pm_ops = {
};
static const struct geni_i2c_desc geni_i2c = {
- .resources_init = geni_i2c_resources_init,
+ .resources_init = geni_se_resources_init,
.set_rate = qcom_geni_i2c_conf,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
@@ -1362,7 +1353,7 @@ static const struct geni_i2c_desc geni_i2c = {
static const struct geni_i2c_desc i2c_master_hub = {
.no_dma_support = true,
.tx_fifo_depth = 16,
- .resources_init = geni_i2c_resources_init,
+ .resources_init = geni_se_resources_init,
.set_rate = qcom_geni_i2c_conf,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 8/8] i2c: qcom-geni: Use geni_se_set_rate() for setting source clock frequency
2026-10-15 12:58 [PATCH v3 0/8] soc: qcom: geni: Derive SE clock configuration from OPP table on SA8255P Praveen Talari
` (6 preceding siblings ...)
2026-10-15 12:58 ` [PATCH v3 7/8] i2c: qcom-geni: Use common GENI resource initialization helper Praveen Talari
@ 2026-10-15 12:58 ` Praveen Talari
7 siblings, 0 replies; 11+ messages in thread
From: Praveen Talari @ 2026-10-15 12:58 UTC (permalink / raw)
To: konrad.dybcio, Sudeep Holla, Cristian Marussi, Ulf Hansson,
Bjorn Andersson, Konrad Dybcio, Greg Kroah-Hartman, Jiri Slaby,
Mark Brown, Viken Dadhaniya, Andi Shyti
Cc: mukesh.savaliya, chandana.chiluveru, arm-scmi, linux-arm-kernel,
linux-pm, linux-kernel, linux-arm-msm, linux-serial, linux-spi,
linux-i2c, Praveen Talari
GENI resource management may be handled either through the Linux clock
framework or a firmware-managed performance domain. Source clock
programming currently depends on platform-specific callbacks.
Use geni_se_set_rate() for source clock programming and perform the
source clock configuration from qcom_geni_i2c_conf(), allowing the GENI
core to abstract the underlying resource-management mechanism from the
I2C driver. This removes the need for platform-specific rate-setting
callbacks and enables a common code path across all GENI I2C platforms.
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com>
Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index dbe344719b86..4879170a1257 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -16,7 +16,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/soc/qcom/geni-se.h>
#include <linux/spinlock.h>
@@ -252,14 +251,14 @@ static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
return -EINVAL;
}
-static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq)
+static int qcom_geni_i2c_conf(struct geni_se *se)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev);
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
u32 val;
int ret;
- ret = dev_pm_opp_set_rate(se->dev, gi2c->src_freq);
+ ret = geni_se_set_rate(&gi2c->se, gi2c->src_freq);
if (ret)
return ret;
@@ -1042,7 +1041,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
return ret;
}
- ret = gi2c->dev_data->set_rate(&gi2c->se, gi2c->clk_freq_out);
+ ret = qcom_geni_i2c_conf(&gi2c->se);
if (ret)
goto out_pm;
@@ -1284,7 +1283,7 @@ static int geni_i2c_runtime_suspend(struct device *dev)
}
}
- return dev_pm_opp_set_rate(dev, 0);
+ return geni_se_set_rate(&gi2c->se, 0);
}
static int geni_i2c_runtime_resume(struct device *dev)
@@ -1299,7 +1298,7 @@ static int geni_i2c_runtime_resume(struct device *dev)
}
if (gi2c->src_freq) {
- ret = dev_pm_opp_set_rate(dev, gi2c->src_freq);
+ ret = geni_se_set_rate(&gi2c->se, gi2c->src_freq);
if (ret)
return ret;
}
@@ -1345,7 +1344,6 @@ static const struct dev_pm_ops geni_i2c_pm_ops = {
static const struct geni_i2c_desc geni_i2c = {
.resources_init = geni_se_resources_init,
- .set_rate = qcom_geni_i2c_conf,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
};
@@ -1354,14 +1352,12 @@ static const struct geni_i2c_desc i2c_master_hub = {
.no_dma_support = true,
.tx_fifo_depth = 16,
.resources_init = geni_se_resources_init,
- .set_rate = qcom_geni_i2c_conf,
.power_on = geni_se_resources_activate,
.power_off = geni_se_resources_deactivate,
};
static const struct geni_i2c_desc sa8255p_geni_i2c = {
.resources_init = geni_se_domain_attach,
- .set_rate = geni_se_set_perf_opp,
};
#ifdef CONFIG_ACPI
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread