* [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM
@ 2026-06-12 4:34 Alexey Klimov
2026-06-12 4:34 ` [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support Alexey Klimov
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Alexey Klimov @ 2026-06-12 4:34 UTC (permalink / raw)
To: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin,
Alim Akhtar, Rafael J. Wysocki, Viresh Kumar
Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm,
kernel-team, linux-kernel, Alexey Klimov
The series implements acpm_do_xfer_fast() that does as little
as possible to make it usabe in atomic context to eventually
use it in cpufreq path with ->fast_switch(). That's basically it.
Cpufreq deals directly with ACPM dvfs instead of going through
clk framework.
This series is Work-In-Progress / RFC.
This is currently a working prototype, but I am sending it as an RFC to
get feedback on the approach — specifically the ACPM fast path of sending
messages. If ACPM part is no-go then cpufreq is not needed.
Known TODO items:
-- rx/tx channels locks rework;
-- initial frequency fetching: Exynos850 firmware currently lacks a
clean get_rate, so the driver falls back to dump defaults until the
first freq transition.
-- potential mailbox channels re-implementation.
Any thoughts, testing, or feedback on the architecture would be greatly
appreciated.
This was tested on Exynos850 with example of DT nodes described
in [1] and [2].
Dependencies are different series that adds Exynos850 ACPM support:
-- ACPM mailbox support for Exynos850:
https://lore.kernel.org/linux-samsung-soc/20260429-exynos850-mbox-dts-v1-1-7f3ad27ed4f4@linaro.org/
-- sram DT node:
https://lore.kernel.org/linux-samsung-soc/20260413-exynos850_sram-v1-1-7fda5b7fb7d4@linaro.org/
-- ACPM support on Exynos850:
https://lore.kernel.org/linux-samsung-soc/20260513-exynos850-acpm-firmware-support-v1-0-3858d097e433@linaro.org/
(the above dependencies pull some other series as well)
[1]:
cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a55";
reg = <0x0>;
clocks = <&acpm_ipc E850_CLK_ACPM_DVFS_CPUCL0>;
enable-method = "psci";
operating-points-v2 = <&cpucl0_opp_table>;
capacity-dmips-mhz = <250>;
dynamic-power-coefficient = <70>;
};
[2]:
cpucl0_opp_table: opp-table-0 {
compatible = "operating-points-v2";
opp-shared;
opp-130000000 {
opp-hz = /bits/ 64 <130000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-182000000 {
opp-hz = /bits/ 64 <182000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-247000000 {
opp-hz = /bits/ 64 <247000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-351000000 {
opp-hz = /bits/ 64 <351000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-442000000 {
opp-hz = /bits/ 64 <442000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-546000000 {
opp-hz = /bits/ 64 <546000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-650000000 {
opp-hz = /bits/ 64 <650000000>;
opp-microvolt = <625000>;
clock-latency-ns = <500000>;
};
opp-806000000 {
opp-hz = /bits/ 64 <806000000>;
opp-microvolt = <656250>;
clock-latency-ns = <500000>;
};
opp-949000000 {
opp-hz = /bits/ 64 <949000000>;
opp-microvolt = <681250>;
clock-latency-ns = <500000>;
};
opp-1053000000 {
opp-hz = /bits/ 64 <1053000000>;
opp-microvolt = <706250>;
clock-latency-ns = <500000>;
};
opp-1157000000 {
opp-hz = /bits/ 64 <1157000000>;
opp-microvolt = <737500>;
clock-latency-ns = <500000>;
};
opp-1300000000 {
opp-hz = /bits/ 64 <1300000000>;
opp-microvolt = <781250>;
clock-latency-ns = <500000>;
};
opp-1456000000 {
opp-hz = /bits/ 64 <1456000000>;
opp-microvolt = <825000>;
clock-latency-ns = <500000>;
};
opp-1586000000 {
opp-hz = /bits/ 64 <1586000000>;
opp-microvolt = <875000>;
clock-latency-ns = <500000>;
};
opp-1742000000 {
opp-hz = /bits/ 64 <1742000000>;
opp-microvolt = <937500>;
clock-latency-ns = <500000>;
};
opp-1846000000 {
opp-hz = /bits/ 64 <1846000000>;
opp-microvolt = <981250>;
clock-latency-ns = <500000>;
};
opp-2002000000 {
opp-hz = /bits/ 64 <2002000000>;
opp-microvolt = <1050000>;
clock-latency-ns = <500000>;
};
opp-2106000000 {
opp-hz = /bits/ 64 <2106000000>;
opp-microvolt = <1106250>;
clock-latency-ns = <500000>;
};
opp-2210000000 {
opp-hz = /bits/ 64 <2210000000>;
opp-microvolt = <1181250>;
clock-latency-ns = <500000>;
};
};
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
---
Alexey Klimov (2):
firmware: samsung: acpm: add fire-and-forget xfer support
cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs
drivers/cpufreq/Kconfig.arm | 8 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/acpm-cpufreq.c | 195 +++++++++++++++++++++
drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
drivers/firmware/samsung/exynos-acpm-dvfs.c | 14 ++
drivers/firmware/samsung/exynos-acpm-dvfs.h | 3 +
drivers/firmware/samsung/exynos-acpm.c | 142 +++++++++++++--
drivers/firmware/samsung/exynos-acpm.h | 3 +
.../linux/firmware/samsung/exynos-acpm-protocol.h | 2 +
9 files changed, 361 insertions(+), 10 deletions(-)
---
base-commit: ec039126b7fac4e3af35ebccaa7c6f9b6875ba81
change-id: 20260612-acpm-fast-xfer-86c4de78c20b
Best regards,
--
Alexey Klimov <alexey.klimov@linaro.org>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support 2026-06-12 4:34 [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov @ 2026-06-12 4:34 ` Alexey Klimov 2026-07-09 10:03 ` Krzysztof Kozlowski 2026-06-12 4:34 ` [PATCH 2/2] cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs Alexey Klimov 2026-06-12 4:36 ` [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov 2 siblings, 1 reply; 6+ messages in thread From: Alexey Klimov @ 2026-06-12 4:34 UTC (permalink / raw) To: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Rafael J. Wysocki, Viresh Kumar Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm, kernel-team, linux-kernel, Alexey Klimov The current ACPM IPC protocol relies on synchronous polling (acpm_dequeue_by_polling) to process mailbox responses. For CPU DVFS, cpufreqs schedutil governor requires ->fast_switch() to execute in an atomic context. Waiting for firmware acknowledgments in a loop in such case also using udelay(20) under spinlock doesn't make a lot of sense. Experiemnts on Exynos850 showed that even with removed udelay() or with it significantly decreased, the firmware processing takes 15us...250us. Introduce acpm_do_xfer_fast(), which implements a fire-and-forget asynchronous path: - utilizes spin_trylock() to exit without sleeping if the channel is busy; - adds/sends the message and kicks the mailbox doorbell; - exits immediately, allowing fast_switch to complete quickly. To prevent the unread asynchronous responses from permanently exhausting the 63-slot sequence ring buffer, implement an acpm_drain_stale_rx(). This drains the RX queue during the fast path: - copies payloads and sets completion flags for sleeping synchronous users; - explicitly acks 'is_async' messages. Hooks it up in the right places of ACPM dvfs machinery. The channels {tx,rx}_lock needs probably a bit of rework to differentiate between channels that support or need fast xfer and those that do not. Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org> --- drivers/firmware/samsung/exynos-acpm-dvfs.c | 14 ++ drivers/firmware/samsung/exynos-acpm-dvfs.h | 3 + drivers/firmware/samsung/exynos-acpm.c | 142 +++++++++++++++++++-- drivers/firmware/samsung/exynos-acpm.h | 3 + .../linux/firmware/samsung/exynos-acpm-protocol.h | 2 + 5 files changed, 154 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c index 7266312ef5a6..5411aa121b73 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.c +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c @@ -43,6 +43,20 @@ int acpm_dvfs_set_rate(struct acpm_handle *handle, return acpm_do_xfer(handle, &xfer); } +int acpm_dvfs_set_rate_fast(struct acpm_handle *handle, + unsigned int acpm_chan_id, unsigned int clk_id, + unsigned long rate) +{ + struct acpm_xfer xfer = {0}; + u32 cmd[4]; + + acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate); + acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false); + + return acpm_do_xfer_fast(handle, &xfer); +} + + static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id) { cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.h b/drivers/firmware/samsung/exynos-acpm-dvfs.h index b37b15426102..107d9aa27690 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.h +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.h @@ -14,6 +14,9 @@ struct acpm_handle; int acpm_dvfs_set_rate(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int id, unsigned long rate); +int acpm_dvfs_set_rate_fast(struct acpm_handle *handle, + unsigned int acpm_chan_id, unsigned int id, + unsigned long rate); unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id); diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 942a2e9f02f5..3caab47adf26 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -20,13 +20,13 @@ #include <linux/mailbox/exynos-message.h> #include <linux/mailbox_client.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/math.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/spinlock.h> #include <linux/types.h> #include "exynos-acpm.h" @@ -109,12 +109,16 @@ struct acpm_queue { * @rxcnt: expected length of the response in 32-bit words. * @completed: flag indicating if the firmware response has been fully * processed. + * @is_async: For fire-and-forget xfer. Set to true to just ack + * responses without processing. + * By default, set to false for regular senders. */ struct acpm_rx_data { u32 *cmd __counted_by_ptr(cmdcnt); size_t cmdcnt; size_t rxcnt; bool completed; + bool is_async; }; #define ACPM_SEQNUM_MAX 64 @@ -148,8 +152,8 @@ struct acpm_chan { struct acpm_info *acpm; struct acpm_queue tx; struct acpm_queue rx; - struct mutex tx_lock; - struct mutex rx_lock; + spinlock_t tx_lock; + spinlock_t rx_lock; unsigned int qlen; unsigned int mlen; @@ -232,7 +236,7 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer, *native_match = false; - guard(mutex)(&achan->rx_lock); + guard(spinlock)(&achan->rx_lock); rx_front = readl(achan->rx.front); i = readl(achan->rx.rear); @@ -297,6 +301,9 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer, *native_match = true; } + if (rx_data->is_async) + clear_bit_unlock(seqnum, achan->bitmap_seqnum); + i = (i + 1) % achan->qlen; } while (i != rx_front); @@ -306,6 +313,57 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer, return 0; } +static void acpm_drain_stale_rx(struct acpm_chan *achan) +{ + u32 rx_front, seqnum, rx_seqnum; + const void __iomem *base = achan->rx.base; + struct acpm_rx_data *rx_data; + u32 i, val, mlen = achan->mlen; + + if (!spin_trylock(&achan->rx_lock)) + return; + + rx_front = readl(achan->rx.front); + i = readl(achan->rx.rear); + + /* Get out quick if we nothing to process */ + if (i == rx_front) { + spin_unlock(&achan->rx_lock); + return; + } + + do { + val = readl(base + mlen * i); + rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, val); + + if (rx_seqnum) { + seqnum = rx_seqnum - 1; + rx_data = &achan->rx_data[seqnum]; + + if (rx_data->rxcnt) + __ioread32_copy(rx_data->cmd, base + mlen * i, rx_data->rxcnt); + + /* Signal the waiting thread (if any). If it hasn't started + * spinning yet, it will see this instantly when it does. */ + smp_store_release(&rx_data->completed, true); + + /* Only free the sequence number if it belongs to an + * async request. Senders who use regular acpm_do_xfer() + * will free their own sequence numbers in + * acpm_dequeue_by_polling(). + */ + if (rx_data->is_async) + clear_bit_unlock(seqnum, achan->bitmap_seqnum); + } + + i = (i + 1) % achan->qlen; + } while (i != rx_front); + + writel(rx_front, achan->rx.rear); + + spin_unlock(&achan->rx_lock); +} + /** * acpm_dequeue_by_polling() - RX dequeue by polling. * @achan: ACPM channel info. @@ -388,15 +446,15 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front) } /** - * acpm_prepare_xfer() - prepare a transfer before writing the message to the + * __acpm_prepare_xfer() - prepare a transfer before writing the message to the * TX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer being prepared. * * Return: 0 on success, -errno otherwise. */ -static int acpm_prepare_xfer(struct acpm_chan *achan, - const struct acpm_xfer *xfer) +static int __acpm_prepare_xfer(struct acpm_chan *achan, + const struct acpm_xfer *xfer, bool is_async) { struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; @@ -429,6 +487,7 @@ static int acpm_prepare_xfer(struct acpm_chan *achan, /* Clear data for upcoming responses */ rx_data = &achan->rx_data[bit]; rx_data->completed = false; + rx_data->is_async = is_async; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->cmdcnt); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; @@ -436,6 +495,12 @@ static int acpm_prepare_xfer(struct acpm_chan *achan, return 0; } +static int acpm_prepare_xfer(struct acpm_chan *achan, + const struct acpm_xfer *xfer) +{ + return __acpm_prepare_xfer(achan, xfer, false); +} + /** * acpm_wait_for_message_response - an helper to group all possible ways of * waiting for a synchronous message response. @@ -452,6 +517,62 @@ static int acpm_wait_for_message_response(struct acpm_chan *achan, return acpm_dequeue_by_polling(achan, xfer); } +int acpm_do_xfer_fast(struct acpm_handle *handle, const struct acpm_xfer *xfer) +{ + struct acpm_info *acpm = handle_to_acpm_info(handle); + struct exynos_mbox_msg msg; + struct acpm_chan *achan; + u32 idx, tx_front; + int ret; + + if (xfer->acpm_chan_id >= acpm->num_chans) + return -EINVAL; + + achan = &acpm->chans[xfer->acpm_chan_id]; + + msg.chan_id = xfer->acpm_chan_id; + msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL; + + /* Ideally should be a raw_spin_trylock. + * If we can't get it immediately, then give up. + */ + if (!spin_trylock(&achan->tx_lock)) + return -EBUSY; + + /* clean up/ack previous responses */ + acpm_drain_stale_rx(achan); + + tx_front = readl(achan->tx.front); + idx = (tx_front + 1) % achan->qlen; + + if (idx == readl(achan->tx.rear)) { + /* stalled; queue is full? */ + spin_unlock(&achan->tx_lock); + return -EBUSY; + } + + ret = __acpm_prepare_xfer(achan, xfer, true); + if (ret) { + spin_unlock(&achan->tx_lock); + return ret; + } + + __iowrite32_copy(achan->tx.base + achan->mlen * tx_front, + xfer->txd, xfer->txcnt); + + /* advance TX front */ + writel(idx, achan->tx.front); + + /* ring the doorbell */ + ret = mbox_send_message(achan->chan, (void *)&msg); + if (ret >= 0) + mbox_client_txdone(achan->chan, 0); + + spin_unlock(&achan->tx_lock); + + return ret < 0 ? ret : 0; +} + /** * acpm_do_xfer() - do one transfer. * @handle: pointer to the acpm handle. @@ -485,7 +606,7 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) msg.chan_id = xfer->acpm_chan_id; msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL; - scoped_guard(mutex, &achan->tx_lock) { + scoped_guard(spinlock, &achan->tx_lock) { tx_front = readl(achan->tx.front); idx = (tx_front + 1) % achan->qlen; @@ -654,8 +775,8 @@ static int acpm_channels_init(struct acpm_info *acpm) if (ret) return ret; - mutex_init(&achan->rx_lock); - mutex_init(&achan->tx_lock); + spin_lock_init(&achan->rx_lock); + spin_lock_init(&achan->tx_lock); cl->dev = dev; @@ -675,6 +796,7 @@ static void acpm_clk_pdev_unregister(void *data) static const struct acpm_ops exynos_acpm_driver_ops = { .dvfs = { .set_rate = acpm_dvfs_set_rate, + .set_rate_fast = acpm_dvfs_set_rate_fast, .get_rate = acpm_dvfs_get_rate, }, diff --git a/drivers/firmware/samsung/exynos-acpm.h b/drivers/firmware/samsung/exynos-acpm.h index 708f6b0102ac..210709326a1f 100644 --- a/drivers/firmware/samsung/exynos-acpm.h +++ b/drivers/firmware/samsung/exynos-acpm.h @@ -22,4 +22,7 @@ void acpm_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdcnt, int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer); +int acpm_do_xfer_fast(struct acpm_handle *handle, + const struct acpm_xfer *xfer); + #endif /* __EXYNOS_ACPM_H__ */ diff --git a/include/linux/firmware/samsung/exynos-acpm-protocol.h b/include/linux/firmware/samsung/exynos-acpm-protocol.h index c6b35c0ff300..93c9b20517f8 100644 --- a/include/linux/firmware/samsung/exynos-acpm-protocol.h +++ b/include/linux/firmware/samsung/exynos-acpm-protocol.h @@ -16,6 +16,8 @@ struct device_node; struct acpm_dvfs_ops { int (*set_rate)(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id, unsigned long rate); + int (*set_rate_fast)(struct acpm_handle *handle, unsigned int acpm_chan_id, + unsigned int clk_id, unsigned long rate); unsigned long (*get_rate)(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id); -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support 2026-06-12 4:34 ` [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support Alexey Klimov @ 2026-07-09 10:03 ` Krzysztof Kozlowski 0 siblings, 0 replies; 6+ messages in thread From: Krzysztof Kozlowski @ 2026-07-09 10:03 UTC (permalink / raw) To: Alexey Klimov, Tudor Ambarus, Sam Protsenko, Peter Griffin, Alim Akhtar, Rafael J. Wysocki, Viresh Kumar Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm, kernel-team, linux-kernel On 12/06/2026 06:34, Alexey Klimov wrote: > > +int acpm_dvfs_set_rate_fast(struct acpm_handle *handle, > + unsigned int acpm_chan_id, unsigned int clk_id, > + unsigned long rate) > +{ > + struct acpm_xfer xfer = {0}; > + u32 cmd[4]; > + > + acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate); > + acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false); > + > + return acpm_do_xfer_fast(handle, &xfer); > +} > + > + Just one blank line > static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id) > { > cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); > diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.h b/drivers/firmware/samsung/exynos-acpm-dvfs.h > index b37b15426102..107d9aa27690 100644 > --- a/drivers/firmware/samsung/exynos-acpm-dvfs.h > +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.h > @@ -14,6 +14,9 @@ struct acpm_handle; > int acpm_dvfs_set_rate(struct acpm_handle *handle, > unsigned int acpm_chan_id, unsigned int id, > unsigned long rate); > +int acpm_dvfs_set_rate_fast(struct acpm_handle *handle, > + unsigned int acpm_chan_id, unsigned int id, > + unsigned long rate); > unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle, > unsigned int acpm_chan_id, ... > + do { > + val = readl(base + mlen * i); > + rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, val); > + > + if (rx_seqnum) { > + seqnum = rx_seqnum - 1; > + rx_data = &achan->rx_data[seqnum]; > + > + if (rx_data->rxcnt) > + __ioread32_copy(rx_data->cmd, base + mlen * i, rx_data->rxcnt); > + > + /* Signal the waiting thread (if any). If it hasn't started Please use Linux style comments. > + * spinning yet, it will see this instantly when it does. */ > + smp_store_release(&rx_data->completed, true); > + > + /* Only free the sequence number if it belongs to an > + * async request. Senders who use regular acpm_do_xfer() > + * will free their own sequence numbers in > + * acpm_dequeue_by_polling(). > + */ > + if (rx_data->is_async) > + clear_bit_unlock(seqnum, achan->bitmap_seqnum); > + } > + > + i = (i + 1) % achan->qlen; > + } while (i != rx_front); > + > + writel(rx_front, achan->rx.rear); > + > + spin_unlock(&achan->rx_lock); > +} > + > /** > * acpm_dequeue_by_polling() - RX dequeue by polling. > * @achan: ACPM channel info. > @@ -388,15 +446,15 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front) > } > ... > + > /** > * acpm_do_xfer() - do one transfer. > * @handle: pointer to the acpm handle. > @@ -485,7 +606,7 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) > msg.chan_id = xfer->acpm_chan_id; > msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL; > > - scoped_guard(mutex, &achan->tx_lock) { > + scoped_guard(spinlock, &achan->tx_lock) { This blind replacement just feels completely wrong. Not only you made huge part of code non-sleepable but actually put spinlock over readl_poll_timeout with 2*500 ms timeout. > tx_front = readl(achan->tx.front); > idx = (tx_front + 1) % achan->qlen; > > @@ -654,8 +775,8 @@ static int acpm_channels_init(struct acpm_info *acpm) > if (ret) > return ret; > Best regards, Krzysztof ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs 2026-06-12 4:34 [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov 2026-06-12 4:34 ` [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support Alexey Klimov @ 2026-06-12 4:34 ` Alexey Klimov 2026-06-12 7:39 ` Viresh Kumar 2026-06-12 4:36 ` [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov 2 siblings, 1 reply; 6+ messages in thread From: Alexey Klimov @ 2026-06-12 4:34 UTC (permalink / raw) To: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Rafael J. Wysocki, Viresh Kumar Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm, kernel-team, linux-kernel, Alexey Klimov Exynos-based SoCs (e.g., Exynos850, gs101) manage CPU DVFS via an ACPM co-processor (sometimes co-processor specifically called APM). Historically, routing CPU frequencies through the clock framework breaks fast frequency switching as it is implemented in cpufreq-dt. The clk_set_rate() uses mutexes, which prevents the scheduler to utilize schedutil's fast path. Introduce a dedicated ACPM-based cpufreq driver that bypasses the clock framework and communicates directly with the ACPM firmware protocol. It implements ->fast_switch() to rely on acpm_dvfs_set_rate_fast(), enabling faster frequency transitions. Add Google gs101 and Samsung exynos850 to the cpufreq-dt-platdev blocklist to prevent driver cpufreq-dt initialisation. Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org> --- drivers/cpufreq/Kconfig.arm | 8 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/acpm-cpufreq.c | 195 +++++++++++++++++++++++++++++++++++ drivers/cpufreq/cpufreq-dt-platdev.c | 3 + 4 files changed, 207 insertions(+) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index a441668f9e0c..891ff4b6ec22 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -24,6 +24,14 @@ config ARM_AIROHA_SOC_CPUFREQ help This adds the CPUFreq driver for Airoha EN7581 SoCs. +config ARM_ACPM_CPUFREQ + tristate "ACPM based CPUFreq support" + depends on ARCH_EXYNOS || (COMPILE_TEST && 64BIT) + select PM_OPP + help + This adds the CPUFreq driver for Exynos-based machines + with ACPM firmware. + config ARM_APPLE_SOC_CPUFREQ tristate "Apple Silicon SoC CPUFreq support" depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 6c7a39b7f8d2..c54d2dd6629d 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o ################################################################################## # ARM SoC drivers obj-$(CONFIG_ARM_AIROHA_SOC_CPUFREQ) += airoha-cpufreq.o +obj-$(CONFIG_ARM_ACPM_CPUFREQ) += acpm-cpufreq.o obj-$(CONFIG_ARM_APPLE_SOC_CPUFREQ) += apple-soc-cpufreq.o obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o diff --git a/drivers/cpufreq/acpm-cpufreq.c b/drivers/cpufreq/acpm-cpufreq.c new file mode 100644 index 000000000000..20fb79169993 --- /dev/null +++ b/drivers/cpufreq/acpm-cpufreq.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Exynos ACPM-based CPUFreq DVFS driver + */ + +#include <linux/cpu.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_opp.h> +#include <linux/slab.h> + +#include <linux/firmware/samsung/exynos-acpm-protocol.h> + +#define ACPM_DVFS_TRANSITION_TIMEOUT 400 + +struct acpm_cpu_priv { + struct device *cpu_dev; + struct acpm_handle *handle; + unsigned int acpm_chan_id; + unsigned int clk_id; +}; + +static unsigned int acpm_soc_cpufreq_get_rate(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); + struct acpm_cpu_priv *priv; + + if (unlikely(!policy)) + return 0; + + priv = policy->driver_data; + + /* return priv->handle->ops->dvfs.get_rate(priv->handle, priv->acpm_chan_id, + * priv->clk_id) / 1000; + */ + + /* TODO: FIXME. Exynos850 doesn't return rate via ACPM */ + return policy->cur; +} + +static int acpm_soc_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int index) +{ + struct acpm_cpu_priv *priv = policy->driver_data; + unsigned long freq_khz = policy->freq_table[index].frequency; + + /* standard slow path */ + return priv->handle->ops->dvfs.set_rate(priv->handle, priv->acpm_chan_id, + priv->clk_id, freq_khz * 1000); +} + +static unsigned int acpm_soc_cpufreq_fast_switch(struct cpufreq_policy *policy, + unsigned int target_freq) +{ + struct acpm_cpu_priv *priv = policy->driver_data; + int ret; + + ret = priv->handle->ops->dvfs.set_rate_fast(priv->handle, priv->acpm_chan_id, + priv->clk_id, target_freq * 1000); + if (ret < 0) + return 0; + + return target_freq; +} + +static int acpm_soc_cpufreq_init(struct cpufreq_policy *policy) +{ + struct device *cpu_dev = get_cpu_device(policy->cpu); + struct cpufreq_frequency_table *freq_table; + struct acpm_cpu_priv *priv; + struct of_phandle_args args; + unsigned int transition_latency; + int ret; + + if (!cpu_dev) + return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = of_parse_phandle_with_args(cpu_dev->of_node, "clocks", + "#clock-cells", 0, &args); + if (ret) { + dev_err(cpu_dev, "failed to parse clocks property\n"); + goto out_free_priv; + } + + priv->clk_id = args.args[0]; + + /* + * DVFS communication is expected to happen only via channel 0 + * for now for known SoCs with ACPM firmware. Hardcoding. + */ + priv->acpm_chan_id = 0; + + priv->handle = devm_acpm_get_by_node(cpu_dev, args.np); + of_node_put(args.np); + + if (IS_ERR(priv->handle)) { + ret = PTR_ERR(priv->handle); + goto out_free_priv; + } + + priv->cpu_dev = cpu_dev; + + ret = dev_pm_opp_of_add_table(cpu_dev); + if (ret < 0) { + dev_err(cpu_dev, "failed to add OPP table: %d\n", ret); + goto out_free_priv; + } + + dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); + + ret = dev_pm_opp_get_opp_count(cpu_dev); + if (ret <= 0) { + ret = -EPROBE_DEFER; + goto out_remove_table; + } + + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); + goto out_remove_table; + } + + policy->driver_data = priv; + policy->freq_table = freq_table; + + transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); + if (!transition_latency) + transition_latency = ACPM_DVFS_TRANSITION_TIMEOUT * NSEC_PER_USEC; + + policy->cpuinfo.transition_latency = transition_latency; + policy->dvfs_possible_from_any_cpu = true; + policy->fast_switch_possible = true; + policy->suspend_freq = freq_table[0].frequency; + + /* TODO: FIXME. Exynos850 doesn't expose rate of clocks via ACPM (get_rate doesn't work) */ + policy->cur = freq_table[0].frequency; + + return 0; + +out_remove_table: + dev_pm_opp_of_remove_table(cpu_dev); +out_free_priv: + kfree(priv); + return ret; +} + +static void acpm_soc_cpufreq_exit(struct cpufreq_policy *policy) +{ + struct acpm_cpu_priv *priv = policy->driver_data; + + dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); + dev_pm_opp_of_remove_table(priv->cpu_dev); + kfree(priv); +} + +static struct cpufreq_driver acpm_soc_cpufreq_driver = { + .name = "acpm-cpufreq", + .flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_IS_COOLING_DEV, + .verify = cpufreq_generic_frequency_table_verify, + .get = acpm_soc_cpufreq_get_rate, + .init = acpm_soc_cpufreq_init, + .exit = acpm_soc_cpufreq_exit, + .target_index = acpm_soc_cpufreq_set_target, + .fast_switch = acpm_soc_cpufreq_fast_switch, + .register_em = cpufreq_register_em_with_opp, + .set_boost = cpufreq_boost_set_sw, + .suspend = cpufreq_generic_suspend, +}; + +static int __init acpm_soc_cpufreq_module_init(void) +{ + if (!of_machine_is_compatible("google,gs101") && + !of_machine_is_compatible("samsung,exynos850")) + return -ENODEV; + + return cpufreq_register_driver(&acpm_soc_cpufreq_driver); +} +module_init(acpm_soc_cpufreq_module_init); + +static void __exit acpm_soc_cpufreq_module_exit(void) +{ + cpufreq_unregister_driver(&acpm_soc_cpufreq_driver); +} +module_exit(acpm_soc_cpufreq_module_exit); + +MODULE_AUTHOR("Alexey Klimov <alexey.klimov@linaro.org>"); +MODULE_DESCRIPTION("ACPM-based CPUfreq DVFS driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index ff1204c666b1..ae58aa92fc40 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -186,6 +186,9 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,sm8550", }, { .compatible = "qcom,sm8650", }, + { .compatible = "google,gs101", }, + { .compatible = "samsung,exynos850", }, + { .compatible = "st,stih407", }, { .compatible = "st,stih410", }, { .compatible = "st,stih418", }, -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs 2026-06-12 4:34 ` [PATCH 2/2] cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs Alexey Klimov @ 2026-06-12 7:39 ` Viresh Kumar 0 siblings, 0 replies; 6+ messages in thread From: Viresh Kumar @ 2026-06-12 7:39 UTC (permalink / raw) To: Alexey Klimov Cc: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Rafael J. Wysocki, Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm, kernel-team, linux-kernel On 12-06-26, 05:34, Alexey Klimov wrote: > Exynos-based SoCs (e.g., Exynos850, gs101) manage CPU DVFS via an > ACPM co-processor (sometimes co-processor specifically called APM). > Historically, routing CPU frequencies through the clock framework > breaks fast frequency switching as it is implemented in cpufreq-dt. > The clk_set_rate() uses mutexes, which prevents the scheduler to > utilize schedutil's fast path. The OPP core can be configured with a "config_clks" helper, which can let you call a custom callback instead of clk_set_rate(). With that you can still use cpufreq-dt ? This may need minor update in OPP core though, as we don't allow the custom callback for single clocks for now. > Introduce a dedicated ACPM-based cpufreq driver that bypasses the clock > framework and communicates directly with the ACPM firmware protocol. > It implements ->fast_switch() to rely on acpm_dvfs_set_rate_fast(), > enabling faster frequency transitions. > > Add Google gs101 and Samsung exynos850 to the cpufreq-dt-platdev > blocklist to prevent driver cpufreq-dt initialisation. > > Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org> > --- > drivers/cpufreq/Kconfig.arm | 8 ++ > drivers/cpufreq/Makefile | 1 + > drivers/cpufreq/acpm-cpufreq.c | 195 +++++++++++++++++++++++++++++++++++ > drivers/cpufreq/cpufreq-dt-platdev.c | 3 + > 4 files changed, 207 insertions(+) > > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm > index a441668f9e0c..891ff4b6ec22 100644 > --- a/drivers/cpufreq/Kconfig.arm > +++ b/drivers/cpufreq/Kconfig.arm > @@ -24,6 +24,14 @@ config ARM_AIROHA_SOC_CPUFREQ > help > This adds the CPUFreq driver for Airoha EN7581 SoCs. > > +config ARM_ACPM_CPUFREQ > + tristate "ACPM based CPUFreq support" > + depends on ARCH_EXYNOS || (COMPILE_TEST && 64BIT) > + select PM_OPP > + help > + This adds the CPUFreq driver for Exynos-based machines > + with ACPM firmware. > + > config ARM_APPLE_SOC_CPUFREQ > tristate "Apple Silicon SoC CPUFreq support" > depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile > index 6c7a39b7f8d2..c54d2dd6629d 100644 > --- a/drivers/cpufreq/Makefile > +++ b/drivers/cpufreq/Makefile > @@ -54,6 +54,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o > ################################################################################## > # ARM SoC drivers > obj-$(CONFIG_ARM_AIROHA_SOC_CPUFREQ) += airoha-cpufreq.o > +obj-$(CONFIG_ARM_ACPM_CPUFREQ) += acpm-cpufreq.o > obj-$(CONFIG_ARM_APPLE_SOC_CPUFREQ) += apple-soc-cpufreq.o > obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o > obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o > diff --git a/drivers/cpufreq/acpm-cpufreq.c b/drivers/cpufreq/acpm-cpufreq.c > new file mode 100644 > index 000000000000..20fb79169993 > --- /dev/null > +++ b/drivers/cpufreq/acpm-cpufreq.c > @@ -0,0 +1,195 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Exynos ACPM-based CPUFreq DVFS driver > + */ > + > +#include <linux/cpu.h> > +#include <linux/cpufreq.h> > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/pm_opp.h> > +#include <linux/slab.h> > + > +#include <linux/firmware/samsung/exynos-acpm-protocol.h> Add this within the earlier list in alphabetic order. > +#define ACPM_DVFS_TRANSITION_TIMEOUT 400 > + > +struct acpm_cpu_priv { > + struct device *cpu_dev; > + struct acpm_handle *handle; > + unsigned int acpm_chan_id; > + unsigned int clk_id; > +}; > + > +static unsigned int acpm_soc_cpufreq_get_rate(unsigned int cpu) > +{ > + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); > + struct acpm_cpu_priv *priv; > + > + if (unlikely(!policy)) > + return 0; > + > + priv = policy->driver_data; > + > + /* return priv->handle->ops->dvfs.get_rate(priv->handle, priv->acpm_chan_id, > + * priv->clk_id) / 1000; > + */ Wrong multiline comment format. > + > + /* TODO: FIXME. Exynos850 doesn't return rate via ACPM */ :( > + return policy->cur; > +} > + > +static int acpm_soc_cpufreq_set_target(struct cpufreq_policy *policy, > + unsigned int index) > +{ > + struct acpm_cpu_priv *priv = policy->driver_data; > + unsigned long freq_khz = policy->freq_table[index].frequency; > + > + /* standard slow path */ > + return priv->handle->ops->dvfs.set_rate(priv->handle, priv->acpm_chan_id, > + priv->clk_id, freq_khz * 1000); > +} > + > +static unsigned int acpm_soc_cpufreq_fast_switch(struct cpufreq_policy *policy, > + unsigned int target_freq) > +{ > + struct acpm_cpu_priv *priv = policy->driver_data; > + int ret; > + > + ret = priv->handle->ops->dvfs.set_rate_fast(priv->handle, priv->acpm_chan_id, > + priv->clk_id, target_freq * 1000); > + if (ret < 0) > + return 0; > + > + return target_freq; > +} > + > +static int acpm_soc_cpufreq_init(struct cpufreq_policy *policy) > +{ > + struct device *cpu_dev = get_cpu_device(policy->cpu); > + struct cpufreq_frequency_table *freq_table; > + struct acpm_cpu_priv *priv; > + struct of_phandle_args args; > + unsigned int transition_latency; > + int ret; > + > + if (!cpu_dev) > + return -ENODEV; > + > + priv = kzalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + ret = of_parse_phandle_with_args(cpu_dev->of_node, "clocks", > + "#clock-cells", 0, &args); > + if (ret) { > + dev_err(cpu_dev, "failed to parse clocks property\n"); > + goto out_free_priv; > + } > + > + priv->clk_id = args.args[0]; > + > + /* > + * DVFS communication is expected to happen only via channel 0 > + * for now for known SoCs with ACPM firmware. Hardcoding. > + */ > + priv->acpm_chan_id = 0; > + > + priv->handle = devm_acpm_get_by_node(cpu_dev, args.np); devm on CPU node is incorrect. The resource won't be freed if you remove this driver's module. > + of_node_put(args.np); > + > + if (IS_ERR(priv->handle)) { > + ret = PTR_ERR(priv->handle); > + goto out_free_priv; > + } > + > + priv->cpu_dev = cpu_dev; > + > + ret = dev_pm_opp_of_add_table(cpu_dev); > + if (ret < 0) { > + dev_err(cpu_dev, "failed to add OPP table: %d\n", ret); > + goto out_free_priv; > + } > + > + dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); Check count before this ? > + ret = dev_pm_opp_get_opp_count(cpu_dev); > + if (ret <= 0) { > + ret = -EPROBE_DEFER; > + goto out_remove_table; > + } > + > + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); > + if (ret) { > + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); > + goto out_remove_table; > + } > + > + policy->driver_data = priv; > + policy->freq_table = freq_table; > + > + transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); > + if (!transition_latency) > + transition_latency = ACPM_DVFS_TRANSITION_TIMEOUT * NSEC_PER_USEC; > + > + policy->cpuinfo.transition_latency = transition_latency; > + policy->dvfs_possible_from_any_cpu = true; > + policy->fast_switch_possible = true; > + policy->suspend_freq = freq_table[0].frequency; > + > + /* TODO: FIXME. Exynos850 doesn't expose rate of clocks via ACPM (get_rate doesn't work) */ This makes the driver broken. I don't think we can merge with this. > + policy->cur = freq_table[0].frequency; > + > + return 0; > + > +out_remove_table: > + dev_pm_opp_of_remove_table(cpu_dev); > +out_free_priv: > + kfree(priv); > + return ret; > +} > + > +static void acpm_soc_cpufreq_exit(struct cpufreq_policy *policy) > +{ > + struct acpm_cpu_priv *priv = policy->driver_data; > + > + dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); > + dev_pm_opp_of_remove_table(priv->cpu_dev); > + kfree(priv); > +} > + > +static struct cpufreq_driver acpm_soc_cpufreq_driver = { > + .name = "acpm-cpufreq", > + .flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY | > + CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_IS_COOLING_DEV, > + .verify = cpufreq_generic_frequency_table_verify, > + .get = acpm_soc_cpufreq_get_rate, > + .init = acpm_soc_cpufreq_init, > + .exit = acpm_soc_cpufreq_exit, > + .target_index = acpm_soc_cpufreq_set_target, > + .fast_switch = acpm_soc_cpufreq_fast_switch, > + .register_em = cpufreq_register_em_with_opp, > + .set_boost = cpufreq_boost_set_sw, > + .suspend = cpufreq_generic_suspend, > +}; > + > +static int __init acpm_soc_cpufreq_module_init(void) > +{ > + if (!of_machine_is_compatible("google,gs101") && > + !of_machine_is_compatible("samsung,exynos850")) > + return -ENODEV; > + > + return cpufreq_register_driver(&acpm_soc_cpufreq_driver); > +} > +module_init(acpm_soc_cpufreq_module_init); > + > +static void __exit acpm_soc_cpufreq_module_exit(void) > +{ > + cpufreq_unregister_driver(&acpm_soc_cpufreq_driver); > +} > +module_exit(acpm_soc_cpufreq_module_exit); > + > +MODULE_AUTHOR("Alexey Klimov <alexey.klimov@linaro.org>"); > +MODULE_DESCRIPTION("ACPM-based CPUfreq DVFS driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c > index ff1204c666b1..ae58aa92fc40 100644 > --- a/drivers/cpufreq/cpufreq-dt-platdev.c > +++ b/drivers/cpufreq/cpufreq-dt-platdev.c > @@ -186,6 +186,9 @@ static const struct of_device_id blocklist[] __initconst = { > { .compatible = "qcom,sm8550", }, > { .compatible = "qcom,sm8650", }, > > + { .compatible = "google,gs101", }, > + { .compatible = "samsung,exynos850", }, > + > { .compatible = "st,stih407", }, > { .compatible = "st,stih410", }, > { .compatible = "st,stih418", }, > > -- > 2.51.0 -- viresh ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM 2026-06-12 4:34 [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov 2026-06-12 4:34 ` [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support Alexey Klimov 2026-06-12 4:34 ` [PATCH 2/2] cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs Alexey Klimov @ 2026-06-12 4:36 ` Alexey Klimov 2 siblings, 0 replies; 6+ messages in thread From: Alexey Klimov @ 2026-06-12 4:36 UTC (permalink / raw) To: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Rafael J. Wysocki, Viresh Kumar Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm, kernel-team, linux-kernel On Fri Jun 12, 2026 at 5:34 AM BST, Alexey Klimov wrote: > The series implements acpm_do_xfer_fast() that does as little > as possible to make it usabe in atomic context to eventually Sorry, I forgot to add RFC tags. This is RFC/work-in-progress series. Thanks, Alexey ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-09 10:03 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-12 4:34 [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov 2026-06-12 4:34 ` [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support Alexey Klimov 2026-07-09 10:03 ` Krzysztof Kozlowski 2026-06-12 4:34 ` [PATCH 2/2] cpufreq: add ACPM-based CPU DVFS driver for Exynos SoCs Alexey Klimov 2026-06-12 7:39 ` Viresh Kumar 2026-06-12 4:36 ` [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM Alexey Klimov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox