Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86
In-Reply-To: <20260511154147.557481-1-ben.horgan@arm.com>

MPAM is able to emulate ABMC, i.e. mbm_event mode, by making memory
bandwidth monitors assignable. Rather than supporting the 'default'
mbm_assign_mode always use 'mbm_event' mode even if there are sufficient
memory bandwidth monitors. The per monitor event configuration is only
provided by resctrl when in 'mbm_event' mode and so only allowing
'mbm_event' mode will make it easier to support per-monitor event
configuration for MPAM. For the moment, the only event supported is
mbm_total_event with no bandwidth type configuration. The 'mbm_assign_mode'
file will still show 'default' when there is no support for memory
bandwidth monitoring.

The monitors need to be allocated from the driver, and mapped to whichever
control/monitor group resctrl wants to use them with.

Add a second array to hold the monitor values indexed by resctrl's cntr_id.

When CDP is in use, two monitors are needed so the available number of
counters halves. Platforms with one monitor will have zero monitors when
CDP is in use.

Co-developed-by: James Morse <james.morse@arm.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
Changes since rfc v1:
abmc enabled even if enough counters
Helpers from dropped free running commits
carry on with zero counters if using cdp
set config bits
use kmalloc_objs
drop tags for rework
Configure mbm_cntr_configurable, mbm_cntr_assign_fixed

Changes since rfc v2:
Don't set mon->assigned_counters to an error pointer
Fix mpam_resctrl_teardown_mon()
Remove free running check
Separate cleanup allocations, e.g. __free(), from the rest
Restrict scope on err in mpam_resctrl_monitor_init()
---
 drivers/resctrl/mpam_internal.h |   6 +-
 drivers/resctrl/mpam_resctrl.c  | 138 +++++++++++++++++++++++++++++++-
 2 files changed, 140 insertions(+), 4 deletions(-)

diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 1914aefdcba9..7a166b395b5a 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -411,7 +411,11 @@ struct mpam_resctrl_res {
 struct mpam_resctrl_mon {
 	struct mpam_class	*class;
 
-	/* per-class data that resctrl needs will live here */
+	/* Array of allocated MBWU monitors, indexed by (closid, rmid). */
+	int			*mbwu_idx_to_mon;
+
+	/* Array of assigned MBWU monitors, indexed by idx argument. */
+	int			*assigned_counters;
 };
 
 static inline int mpam_alloc_csu_mon(struct mpam_class *class)
diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index f70fa65d39e4..cba295621f56 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -75,6 +75,8 @@ static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready);
  */
 static bool resctrl_enabled;
 
+static unsigned int l3_num_allocated_mbwu = ~0;
+
 bool resctrl_arch_alloc_capable(void)
 {
 	struct mpam_resctrl_res *res;
@@ -140,7 +142,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
 
 bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r)
 {
-	return false;
+	return (r == &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res);
 }
 
 int resctrl_arch_mbm_cntr_assign_set(struct rdt_resource *r, bool enable)
@@ -185,6 +187,22 @@ static void resctrl_reset_task_closids(void)
 	read_unlock(&tasklist_lock);
 }
 
+static void mpam_resctrl_monitor_sync_abmc_vals(struct rdt_resource *l3)
+{
+	l3->mon.num_mbm_cntrs = l3_num_allocated_mbwu;
+	if (cdp_enabled)
+		l3->mon.num_mbm_cntrs /= 2;
+
+	/*
+	 * Continue as normal even if there are zero counters to avoid giving
+	 * resctrl mixed messages.
+	 */
+	l3->mon.mbm_cntr_assignable = true;
+	l3->mon.mbm_assign_on_mkdir = true;
+	l3->mon.mbm_cntr_configurable = false;
+	l3->mon.mbm_cntr_assign_fixed = true;
+}
+
 int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
 {
 	u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID;
@@ -244,6 +262,7 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
 	WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current));
 
 	resctrl_reset_task_closids();
+	mpam_resctrl_monitor_sync_abmc_vals(l3);
 
 	for_each_possible_cpu(cpu)
 		mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0);
@@ -613,6 +632,9 @@ static bool class_has_usable_mbwu(struct mpam_class *class)
 	if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
 		return false;
 
+	if (!cprops->num_mbwu_mon)
+		return false;
+
 	return true;
 }
 
@@ -935,6 +957,52 @@ static void mpam_resctrl_pick_mba(void)
 	}
 }
 
+static void __free_mbwu_mon(struct mpam_class *class, int *array,
+			    u16 num_mbwu_mon)
+{
+	for (int i = 0; i < num_mbwu_mon; i++) {
+		if (array[i] < 0)
+			continue;
+
+		mpam_free_mbwu_mon(class, array[i]);
+		array[i] = ~0;
+	}
+}
+
+static int __alloc_mbwu_mon(struct mpam_class *class, int *array,
+			    u16 num_mbwu_mon)
+{
+	for (int i = 0; i < num_mbwu_mon; i++) {
+		int mbwu_mon = mpam_alloc_mbwu_mon(class);
+
+		if (mbwu_mon < 0) {
+			__free_mbwu_mon(class, array, num_mbwu_mon);
+			return mbwu_mon;
+		}
+		array[i] = mbwu_mon;
+	}
+
+	l3_num_allocated_mbwu = min(l3_num_allocated_mbwu, num_mbwu_mon);
+
+	return 0;
+}
+
+static int *__alloc_mbwu_array(struct mpam_class *class, u16 num_mbwu_mon)
+{
+	int err;
+
+	int *array __free(kfree) = kmalloc_objs(*array, num_mbwu_mon);
+	if (!array)
+		return ERR_PTR(-ENOMEM);
+
+	memset(array, -1, num_mbwu_mon * sizeof(*array));
+
+	err = __alloc_mbwu_mon(class, array, num_mbwu_mon);
+	if (err)
+		return ERR_PTR(err);
+	return_ptr(array);
+}
+
 static void counter_update_class(enum resctrl_event_id evt_id,
 				 struct mpam_class *class)
 {
@@ -1089,6 +1157,38 @@ static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp)
 	return comp->comp_id;
 }
 
+/*
+ * This must run after all event counters have been picked so that any free
+ * running counters have already been allocated.
+ */
+static int mpam_resctrl_monitor_init_abmc(struct mpam_resctrl_mon *mon)
+{
+	struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
+	struct rdt_resource *l3 = &res->resctrl_res;
+	struct mpam_class *class = mon->class;
+	u16 num_mbwu_mon;
+	size_t num_rmid = resctrl_arch_system_num_rmid_idx();
+	int *cntrs;
+
+	int *rmid_array __free(kfree) = kmalloc_objs(*rmid_array, num_rmid);
+	if (!rmid_array) {
+		pr_debug("Failed to allocate RMID array\n");
+		return -ENOMEM;
+	}
+	memset(rmid_array, -1, num_rmid * sizeof(*rmid_array));
+
+	num_mbwu_mon = class->props.num_mbwu_mon;
+	cntrs = __alloc_mbwu_array(mon->class, num_mbwu_mon);
+	if (IS_ERR(cntrs))
+		return PTR_ERR(cntrs);
+	mon->assigned_counters = cntrs;
+	mon->mbwu_idx_to_mon = no_free_ptr(rmid_array);
+
+	mpam_resctrl_monitor_sync_abmc_vals(l3);
+
+	return 0;
+}
+
 static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 				     enum resctrl_event_id type)
 {
@@ -1133,8 +1233,21 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 	 */
 	l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
 
-	if (resctrl_enable_mon_event(type, false, 0, NULL))
-		l3->mon_capable = true;
+	if (type == QOS_L3_MBM_TOTAL_EVENT_ID) {
+		int err;
+
+		err = mpam_resctrl_monitor_init_abmc(mon);
+		if (err)
+			return err;
+
+		static_assert(MAX_EVT_CONFIG_BITS == 0x7f);
+		l3->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
+	}
+
+	if (!resctrl_enable_mon_event(type, false, 0, NULL))
+		return -EINVAL;
+
+	l3->mon_capable = true;
 
 	return 0;
 }
@@ -1697,6 +1810,23 @@ void mpam_resctrl_exit(void)
 	resctrl_exit();
 }
 
+static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
+{
+	u32 num_mbwu_mon = l3_num_allocated_mbwu;
+
+	if (mon->mbwu_idx_to_mon)
+		return;
+
+	if (mon->assigned_counters) {
+		__free_mbwu_mon(class, mon->assigned_counters, num_mbwu_mon);
+		kfree(mon->assigned_counters);
+		mon->assigned_counters = NULL;
+	}
+
+	kfree(mon->mbwu_idx_to_mon);
+	mon->mbwu_idx_to_mon = NULL;
+}
+
 /*
  * The driver is detaching an MSC from this class, if resctrl was using it,
  * pull on resctrl_exit().
@@ -1719,6 +1849,8 @@ void mpam_resctrl_teardown_class(struct mpam_class *class)
 	for_each_mpam_resctrl_mon(mon, eventid) {
 		if (mon->class == class) {
 			mon->class = NULL;
+
+			mpam_resctrl_teardown_mon(mon, class);
 			break;
 		}
 	}
-- 
2.43.0



^ permalink raw reply related

* [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86, Shaopeng Tan
In-Reply-To: <20260511154147.557481-1-ben.horgan@arm.com>

From: James Morse <james.morse@arm.com>

resctrl has two types of counters, NUMA-local and global. MPAM can only
count global either using MSC at the L3 cache or in the memory controllers.
When global and local equate to the same thing continue just to call it
global.

Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Tested-by: Zeng Heng <zengheng4@huawei.com>
Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
Changes since rfc v1:
Move finding any_mon_comp into monitor boilerplate patch
Move mpam_resctrl_get_domain_from_cpu() into monitor boilerplate
Remove free running check
Trim commit message
---
 drivers/resctrl/mpam_resctrl.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 226ff6f532fa..f70fa65d39e4 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -606,6 +606,16 @@ static bool cache_has_usable_csu(struct mpam_class *class)
 	return true;
 }
 
+static bool class_has_usable_mbwu(struct mpam_class *class)
+{
+	struct mpam_props *cprops = &class->props;
+
+	if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
+		return false;
+
+	return true;
+}
+
 /*
  * Calculate the worst-case percentage change from each implemented step
  * in the control.
@@ -983,6 +993,22 @@ static void mpam_resctrl_pick_counters(void)
 				break;
 			}
 		}
+
+		if (class_has_usable_mbwu(class) &&
+		    topology_matches_l3(class) &&
+		    traffic_matches_l3(class)) {
+			pr_debug("class %u has usable MBWU, and matches L3 topology and traffic\n",
+				 class->level);
+
+			/*
+			 * We can't distinguish traffic by destination so
+			 * we don't know if it's staying on the same NUMA
+			 * node. Hence, we can't calculate mbm_local except
+			 * when we only have one L3 and it's equivalent to
+			 * mbm_total and so always use mbm_total.
+			 */
+			counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
+		}
 	}
 }
 
-- 
2.43.0



^ permalink raw reply related

* [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC)
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

Removing the rfc tag as the resctrl precursors [1] have been queued in tip
x86/cache. Due to that dependency, it would be good for this to also go through
x86/cache.

This series adds support for memory bandwidth monitoring.

Please review and test.

Changelogs in patches.

[1] https://lore.kernel.org/all/20260506082855.3694761-1-ben.horgan@arm.com/

Description from the initial cover letter:

The MPAM counter assignment (ABMC emulation) changes that were dropped from
the resctrl glue series due to some missing precursors in resctrl. Counter
assignment enables bandwidth monitoring in systems that have fewer
monitors than resctrl monitor groups.

rfc v1: https://lore.kernel.org/lkml/20260225205436.3571756-1-ben.horgan@arm.com/
rfc v2: https://lore.kernel.org/lkml/20260319165540.381410-1-ben.horgan@arm.com/

Ben Horgan (2):
  arm_mpam: resctrl: Pre-allocate assignable monitors
  arm64: mpam: Add memory bandwidth usage (MBWU) documentation

James Morse (3):
  arm_mpam: resctrl: Pick classes for use as mbm counters
  arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use
  arm_mpam: resctrl: Add resctrl_arch_cntr_read() &
    resctrl_arch_reset_cntr()

 Documentation/arch/arm64/mpam.rst |  17 ++
 drivers/resctrl/mpam_internal.h   |   6 +-
 drivers/resctrl/mpam_resctrl.c    | 306 +++++++++++++++++++++++++++---
 3 files changed, 306 insertions(+), 23 deletions(-)

-- 
2.43.0



^ permalink raw reply

* Re: [PATCH] mmc: davinci: avoid NULL deref of host->data in IRQ handler
From: Ulf Hansson @ 2026-05-11 15:40 UTC (permalink / raw)
  To: Stepan Ionichev; +Cc: ulfh, brgl, linux-mmc, linux-arm-kernel, linux-kernel
In-Reply-To: <20260506190538.596-1-sozdayvek@gmail.com>

On Wed, 6 May 2026 at 21:05, Stepan Ionichev <sozdayvek@gmail.com> wrote:
>
> mmc_davinci_irq() returns early only when both host->cmd and
> host->data are NULL:
>
>   if (host->cmd == NULL && host->data == NULL) {
>           ...
>           return IRQ_NONE;
>   }
>
> So we may legitimately reach the rest of the handler with
> host->data == NULL (and therefore data == NULL). The DATDNE branch
> already guards against this with an explicit "if (data != NULL)"
> check, but the subsequent TOUTRD ("read data timeout") and
> CRCWR/CRCRD ("data CRC error") branches dereference data
> unconditionally:
>
>   if (qstatus & MMCST0_TOUTRD) {
>           data->error = -ETIMEDOUT;        <-- NULL deref
>           ...
>           davinci_abort_data(host, data);
>   }
>
>   if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
>           data->error = -EILSEQ;           <-- NULL deref
>           ...
>   }
>
> If either bit is set in qstatus while host->data is NULL, the kernel
> will crash inside the IRQ handler. smatch flags this:
>
>   drivers/mmc/host/davinci_mmc.c:933 mmc_davinci_irq() error: we
>     previously assumed 'data' could be null (see line 914)
>
> Gate both branches on a non-NULL data, matching the existing pattern
> used by the DATDNE branch.
>
> No functional change for callers where data is non-NULL, which is
> the only case in which these branches did meaningful work before
> this change.
>
> Signed-off-by: Stepan Ionichev <sozdayvek@gmail.com>

Applied for next, thanks!

Kind regards
Uffe


> ---
>  drivers/mmc/host/davinci_mmc.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
> index 42b0118a4..42ad87aa4 100644
> --- a/drivers/mmc/host/davinci_mmc.c
> +++ b/drivers/mmc/host/davinci_mmc.c
> @@ -928,7 +928,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
>                 }
>         }
>
> -       if (qstatus & MMCST0_TOUTRD) {
> +       if (data && (qstatus & MMCST0_TOUTRD)) {
>                 /* Read data timeout */
>                 data->error = -ETIMEDOUT;
>                 end_transfer = 1;
> @@ -940,7 +940,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
>                 davinci_abort_data(host, data);
>         }
>
> -       if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
> +       if (data && (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD))) {
>                 /* Data CRC error */
>                 data->error = -EILSEQ;
>                 end_transfer = 1;
> --
> 2.43.0
>


^ permalink raw reply

* Re: [PATCH v2 2/7] mmc: renesas_sdhi: add R-Car M3Le compatibility string
From: Ulf Hansson @ 2026-05-11 15:39 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Brian Masney, Conor Dooley, Geert Uytterhoeven,
	Krzysztof Kozlowski, Kuninori Morimoto, Magnus Damm,
	Michael Turquette, Rob Herring, Stephen Boyd, Ulf Hansson,
	Wolfram Sang, devicetree, linux-clk, linux-kernel, linux-mmc,
	linux-renesas-soc
In-Reply-To: <20260504144534.43745-3-marek.vasut+renesas@mailbox.org>

On Mon, 4 May 2026 at 16:46, Marek Vasut
<marek.vasut+renesas@mailbox.org> wrote:
>
> Add support for the SD Card/MMC Interface in the Renesas R-Car M3Le
> (R8A779MD) SoC. R19UH0260EJ0100 Rev.1.00 , Dec 25, 2025 Notes 7.70.
> indicates that HS400 mode is not supported.
>
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

Applied for next, thanks!

Kind regards
Uffe


> ---
> Cc: Brian Masney <bmasney@redhat.com>
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: Ulf Hansson <ulfh@kernel.org>
> Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Cc: devicetree@vger.kernel.org
> Cc: linux-clk@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-mmc@vger.kernel.org
> Cc: linux-renesas-soc@vger.kernel.org
> ---
> V2: - New patch
> ---
>  drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> index f6ebb7bc7ede1..b716a518f2650 100644
> --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> @@ -285,6 +285,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
>         { .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, },
>         { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
>         { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
> +       { .compatible = "renesas,sdhi-r8a779md", .data = &of_rcar_gen3_nohs400_compatible, },
>         { .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, },
>         { .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, },
>         { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, },
> --
> 2.53.0
>


^ permalink raw reply

* Re: [PATCH] mmc: host: Move MODULE_DEVICE_TABLE next to the table itself
From: Ulf Hansson @ 2026-05-11 15:39 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Robert Richter, Ulf Hansson, Alex Dubov, Alexey Charkov,
	Krzysztof Kozlowski, linux-mmc, linux-kernel, linux-arm-kernel
In-Reply-To: <20260505103323.207010-2-krzysztof.kozlowski@oss.qualcomm.com>

On Tue, 5 May 2026 at 12:33, Krzysztof Kozlowski
<krzysztof.kozlowski@oss.qualcomm.com> wrote:
>
> By convention MODULE_DEVICE_TABLE() immediately follows the ID table it
> exports, because this is easier to read and verify.  It also makes more
> sense since #ifdef for ACPI or OF could hide both of them.
>
> Most of the privers already have this correctly placed, so adjust
> the missing ones.  No functional impact.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Applied for next, thanks!

Kind regards
Uffe


> ---
>  drivers/mmc/host/cavium-thunderx.c | 2 +-
>  drivers/mmc/host/tifm_sd.c         | 2 +-
>  drivers/mmc/host/wmt-sdmmc.c       | 2 +-
>  3 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c
> index 1373deb3f531..84ff6d82ae3c 100644
> --- a/drivers/mmc/host/cavium-thunderx.c
> +++ b/drivers/mmc/host/cavium-thunderx.c
> @@ -188,6 +188,7 @@ static const struct pci_device_id thunder_mmc_id_table[] = {
>         { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa010) },
>         { 0, }  /* end of table */
>  };
> +MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table);
>
>  static struct pci_driver thunder_mmc_driver = {
>         .name = KBUILD_MODNAME,
> @@ -201,4 +202,3 @@ module_pci_driver(thunder_mmc_driver);
>  MODULE_AUTHOR("Cavium Inc.");
>  MODULE_DESCRIPTION("Cavium ThunderX eMMC Driver");
>  MODULE_LICENSE("GPL");
> -MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table);
> diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
> index c1f7d5b37911..aebffd3ebf60 100644
> --- a/drivers/mmc/host/tifm_sd.c
> +++ b/drivers/mmc/host/tifm_sd.c
> @@ -1044,6 +1044,7 @@ static int tifm_sd_resume(struct tifm_dev *sock)
>  static struct tifm_device_id tifm_sd_id_tbl[] = {
>         { TIFM_TYPE_SD }, { }
>  };
> +MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);
>
>  static struct tifm_driver tifm_sd_driver = {
>         .driver = {
> @@ -1070,7 +1071,6 @@ static void __exit tifm_sd_exit(void)
>  MODULE_AUTHOR("Alex Dubov");
>  MODULE_DESCRIPTION("TI FlashMedia SD driver");
>  MODULE_LICENSE("GPL");
> -MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);
>  MODULE_VERSION(DRIVER_VERSION);
>
>  module_init(tifm_sd_init);
> diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
> index 1b1d691e19fc..489daee4f4fc 100644
> --- a/drivers/mmc/host/wmt-sdmmc.c
> +++ b/drivers/mmc/host/wmt-sdmmc.c
> @@ -744,6 +744,7 @@ static const struct of_device_id wmt_mci_dt_ids[] = {
>         { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps },
>         { /* Sentinel */ },
>  };
> +MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids);
>
>  static int wmt_mci_probe(struct platform_device *pdev)
>  {
> @@ -980,4 +981,3 @@ module_platform_driver(wmt_mci_driver);
>  MODULE_DESCRIPTION("Wondermedia MMC/SD Driver");
>  MODULE_AUTHOR("Tony Prisk");
>  MODULE_LICENSE("GPL v2");
> -MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids);
> --
> 2.51.0
>


^ permalink raw reply

* Re: [PATCH v2 1/7] dt-bindings: mmc: renesas,sdhi: Document R-Car M3Le support
From: Ulf Hansson @ 2026-05-11 15:39 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Brian Masney, Conor Dooley, Geert Uytterhoeven,
	Krzysztof Kozlowski, Kuninori Morimoto, Magnus Damm,
	Michael Turquette, Rob Herring, Stephen Boyd, Ulf Hansson,
	Wolfram Sang, devicetree, linux-clk, linux-kernel, linux-mmc,
	linux-renesas-soc
In-Reply-To: <20260504144534.43745-2-marek.vasut+renesas@mailbox.org>

On Mon, 4 May 2026 at 16:46, Marek Vasut
<marek.vasut+renesas@mailbox.org> wrote:
>
> Document support for the SD Card/MMC Interface in the Renesas R-Car M3Le
> (R8A779MD) SoC.
>
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

Applied for next, thanks!

Kind regards
Uffe


> ---
> Cc: Brian Masney <bmasney@redhat.com>
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: Ulf Hansson <ulfh@kernel.org>
> Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Cc: devicetree@vger.kernel.org
> Cc: linux-clk@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-mmc@vger.kernel.org
> Cc: linux-renesas-soc@vger.kernel.org
> ---
> V2: - New patch
> ---
>  Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
> index 64fac0d11329a..4d66966ce2900 100644
> --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
> +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
> @@ -52,6 +52,7 @@ properties:
>                - renesas,sdhi-r8a77980  # R-Car V3H
>                - renesas,sdhi-r8a77990  # R-Car E3
>                - renesas,sdhi-r8a77995  # R-Car D3
> +              - renesas,sdhi-r8a779md  # R-Car M3Le
>            - const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
>        - items:
>            - enum:
> --
> 2.53.0
>


^ permalink raw reply

* Re: [PATCH v3 0/2] Configuring DMA threshold value for DW-MMC controllers
From: Ulf Hansson @ 2026-05-11 15:39 UTC (permalink / raw)
  To: Kaustabh Chakraborty
  Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin, Krzysztof Kozlowski, Alim Akhtar,
	linux-mmc, devicetree, linux-kernel, linux-arm-kernel,
	linux-samsung-soc
In-Reply-To: <20260415-dwmmc-dma-thr-v3-0-31014d36b6ee@disroot.org>

On Wed, 15 Apr 2026 at 17:03, Kaustabh Chakraborty
<kauschluss@disroot.org> wrote:
>
> In Samsung Exynos 7870 devices with Broadcom Wi-Fi, it has been observed
> that small sized DMA transfers are unreliable and are not written
> properly, which renders the cache incoherent.
>
> Experimental observations say that DMA transfer sizes of somewhere
> around 64 to 512 are intolerable. We must thus implement a mechanism to
> fall back to PIO transfer in this case. One such approach, which this
> series implements is allowing the DMA transfer threshold, which is
> already defined in the driver, to be configurable.
>
> Note that this patch is likely to be labelled as a workaround. These
> smaller transfers seem to be successful from downstream kernels,
> however efforts to figure out how so went in vain. It is also very
> possible that the downstream Broadcom Wi-Fi SDIO driver uses PIO
> transfers as well.
>
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> ---
> Changes in v3:
> - Move host->dma_threshold default to dw_mci_alloc_host() (Shawn Lin)
> - Move comment to document struct dw_mci::dma_threshold (Shawn Lin)
> - Link to v2: https://lore.kernel.org/r/20260414-dwmmc-dma-thr-v2-0-4058078f5361@disroot.org

The v3 series applied for next, thanks!

Kind regards
Uffe


>
> Changes in v2:
> - Remove dt-binding to set DMA threshold (Krzysztof Kozlowski)
> - Add comment to describe struct dw_mci::dma_threshold (Shawn Lin)
> - Set DMA threshold in Exynos 7870 DW-MMC driver (Krzysztof Kozlowski)
> - Link to v1: https://lore.kernel.org/r/20260412-dwmmc-dma-thr-v1-0-75a2f658eee3@disroot.org
>
> ---
> Kaustabh Chakraborty (2):
>       mmc: dw_mmc: implement option for configuring DMA threshold
>       mmc: dw_mmc: exynos: increase DMA threshold value for exynos7870
>
>  drivers/mmc/host/dw_mmc-exynos.c | 1 +
>  drivers/mmc/host/dw_mmc.c        | 4 ++--
>  drivers/mmc/host/dw_mmc.h        | 2 ++
>  3 files changed, 5 insertions(+), 2 deletions(-)
> ---
> base-commit: 1c7cc4904160c6fc6377564140062d68a3dc93a0
> change-id: 20260412-dwmmc-dma-thr-1090d8285ea7
>
> Best regards,
> --
> Kaustabh Chakraborty <kauschluss@disroot.org>
>


^ permalink raw reply

* Re: [PATCH 08/10] clk: amlogic: Add A9 PLL clock controller driver
From: Brian Masney @ 2026-05-11 15:36 UTC (permalink / raw)
  To: jian.hu
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Neil Armstrong, Jerome Brunet, Xianwei Zhao,
	Kevin Hilman, Martin Blumenstingl, linux-kernel, linux-clk,
	devicetree, linux-amlogic, linux-arm-kernel
In-Reply-To: <20260511-b4-a9_clk-v1-8-41cb4071b7c9@amlogic.com>

Hi Jian,

On Mon, May 11, 2026 at 08:47:30PM +0800, Jian Hu via B4 Relay wrote:
> From: Jian Hu <jian.hu@amlogic.com>
> 
> Add the PLL clock controller driver for the Amlogic A9 SoC family.
> 
> Signed-off-by: Jian Hu <jian.hu@amlogic.com>
> ---
>  drivers/clk/meson/Kconfig  |  13 +
>  drivers/clk/meson/Makefile |   1 +
>  drivers/clk/meson/a9-pll.c | 831 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 845 insertions(+)
> 
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index cf8cf3f9e4ee..3549e67d6988 100644
> --- a/drivers/clk/meson/Kconfig
> +++ b/drivers/clk/meson/Kconfig
> @@ -132,6 +132,19 @@ config COMMON_CLK_A1_PERIPHERALS
>  	  device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>  	  controller to work.
>  
> +config COMMON_CLK_A9_PLL
> +	tristate "Amlogic A9 SoC PLL controller support"
> +	depends on ARM64

depends on ARM64 || COMPILE_TEST

> +	default ARCH_MESON
> +	select COMMON_CLK_MESON_REGMAP
> +	select COMMON_CLK_MESON_CLKC_UTILS
> +	select COMMON_CLK_MESON_PLL
> +	imply COMMON_CLK_SCMI
> +	help
> +	  Support for the PLL clock controller on Amlogic A311Y3 based
> +	  device, AKA A9. PLLs are required by most peripheral to operate.
> +	  Say Y if you want A9 PLL clock controller to work.
> +
>  config COMMON_CLK_C3_PLL
>  	tristate "Amlogic C3 PLL clock controller"
>  	depends on ARM64
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index c6719694a242..77636033061f 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>  obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>  obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>  obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
> +obj-$(CONFIG_COMMON_CLK_A9_PLL) += a9-pll.o
>  obj-$(CONFIG_COMMON_CLK_C3_PLL) += c3-pll.o
>  obj-$(CONFIG_COMMON_CLK_C3_PERIPHERALS) += c3-peripherals.o
>  obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
> diff --git a/drivers/clk/meson/a9-pll.c b/drivers/clk/meson/a9-pll.c
> new file mode 100644
> index 000000000000..84b591c3afff
> --- /dev/null
> +++ b/drivers/clk/meson/a9-pll.c
> @@ -0,0 +1,831 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
> +/*
> + * Copyright (C) 2026 Amlogic, Inc. All rights reserved
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/platform_device.h>
> +#include <dt-bindings/clock/amlogic,a9-pll-clkc.h>
> +#include "clk-regmap.h"
> +#include "clk-pll.h"
> +#include "meson-clkc-utils.h"

Sort the headers

> +
> +#define GP0PLL_CTRL0			0x00
> +#define GP0PLL_CTRL1			0x04
> +#define GP0PLL_CTRL2			0x08
> +#define GP0PLL_CTRL3			0x0c
> +#define GP0PLL_CTRL4			0x10
> +
> +/* HIFI0 and HIFI1 share the same IP and register offset layout. */
> +#define HIFIPLL_CTRL0			0x00
> +#define HIFIPLL_CTRL1			0x04
> +#define HIFIPLL_CTRL2			0x08
> +#define HIFIPLL_CTRL3			0x0c
> +#define HIFIPLL_CTRL4			0x10
> +
> +/* MCLK0 and MCLK1 share the same IP and register offset layout. */
> +#define MCLKPLL_CTRL0			0x00
> +#define MCLKPLL_CTRL1			0x04
> +#define MCLKPLL_CTRL2			0x08
> +#define MCLKPLL_CTRL3			0x0c
> +#define MCLKPLL_CTRL4			0x10
> +
> +#define A9_COMP_SEL(_name, _reg, _shift, _mask, _pdata) \
> +	MESON_COMP_SEL(a9_, _name, _reg, _shift, _mask, _pdata, NULL, 0, 0)
> +
> +#define A9_COMP_DIV(_name, _reg, _shift, _width) \
> +	MESON_COMP_DIV(a9_, _name, _reg, _shift, _width, 0, CLK_SET_RATE_PARENT)
> +
> +#define A9_COMP_GATE(_name, _reg, _bit) \
> +	MESON_COMP_GATE(a9_, _name, _reg, _bit, CLK_SET_RATE_PARENT)
> +
> +/*
> + * Compared with previous SoC PLLs, the A9 PLL input path has an inherent
> + * 2-divider. The N pre-divider follows the same calculation rule as OD,
> + * where the pre-divider ratio equals 2^N.
> + *
> + * A9 PLL is composed as follows:
> + *
> + *                      PLL
> + *         +---------------------------------+
> + *         |                                 |
> + *         |             +--+                |
> + *  in/2 >>---[ /2^N ]-->|  |      +-----+   |
> + *         |             |  |------| DCO |----->> out
> + *         |  +--------->|  |      +--v--+   |
> + *         |  |          +--+         |      |
> + *         |  |                       |      |
> + *         |  +--[ *(M + (F/Fmax) ]<--+      |
> + *         |                                 |
> + *         +---------------------------------+
> + *
> + * out = in / 2  * (m + frac / frac_max) / 2^n
> + */
> +
> +static struct clk_fixed_factor a9_gp0_in_div2_div = {
> +	.mult = 1,
> +	.div = 2,
> +	.hw.init = &(struct clk_init_data){
> +		.name = "gp0_in_div2_div",
> +		.ops = &clk_fixed_factor_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},

You can use CLK_HW_INIT_FW_NAME() for the hw.init here and other places
below.

> +};
> +
> +static struct clk_regmap a9_gp0_in_div2 = {
> +	.data = &(struct clk_regmap_gate_data) {
> +		.offset = GP0PLL_CTRL0,
> +		.bit_idx = 27,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "gp0_in_div2",
> +		.ops = &clk_regmap_gate_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_gp0_in_div2_div.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +/* The output frequency range of the A9 PLL_DCO is 1.4 GHz to 2.8 GHz. */
> +static const struct pll_mult_range a9_pll_mult_range = {
> +	.min = 117,
> +	.max = 233,
> +};
> +
> +static const struct reg_sequence a9_gp0_pll_init_regs[] = {
> +	{ .reg = GP0PLL_CTRL0, .def = 0x00010000 },
> +	{ .reg = GP0PLL_CTRL1, .def = 0x11480000 },
> +	{ .reg = GP0PLL_CTRL2, .def = 0x1219b010 },
> +	{ .reg = GP0PLL_CTRL3, .def = 0x00008010 }
> +};
> +
> +static struct clk_regmap a9_gp0_pll_dco = {
> +	.data = &(struct meson_clk_pll_data) {
> +		.en = {
> +			.reg_off = GP0PLL_CTRL0,
> +			.shift   = 28,
> +			.width   = 1,
> +		},
> +		.m = {
> +			.reg_off = GP0PLL_CTRL0,
> +			.shift   = 0,
> +			.width   = 9,
> +		},
> +		.n = {
> +			.reg_off = GP0PLL_CTRL0,
> +			.shift   = 12,
> +			.width   = 3,
> +		},
> +		.frac = {
> +			.reg_off = GP0PLL_CTRL1,
> +			.shift   = 0,
> +			.width   = 17,
> +		},
> +		.l = {
> +			.reg_off = GP0PLL_CTRL0,
> +			.shift   = 31,
> +			.width   = 1,
> +		},
> +		.rst = {
> +			.reg_off = GP0PLL_CTRL0,
> +			.shift   = 29,
> +			.width   = 1,
> +		},
> +		.l_detect = {
> +			.reg_off = GP0PLL_CTRL0,
> +			.shift   = 30,
> +			.width   = 1,
> +		},
> +		.range = &a9_pll_mult_range,
> +		.init_regs = a9_gp0_pll_init_regs,
> +		.init_count = ARRAY_SIZE(a9_gp0_pll_init_regs),
> +		.flags = CLK_MESON_PLL_RST_ACTIVE_LOW |
> +			 CLK_MESON_PLL_N_POWER_OF_TWO |
> +			 CLK_MESON_PLL_L_DETECT_ACTIVE_HIGH,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "gp0_pll_dco",
> +		.ops = &meson_clk_pll_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_gp0_in_div2.hw
> +		},
> +		.num_parents = 1,
> +	},

You can use CLK_HW_INIT_HWS() here and other places below.

Brian


> +};
> +
> +/* For gp0, hifi and mclk pll, the maximum value of od is 4. */
> +static const struct clk_div_table a9_pll_od_table[] = {
> +	{ 0,  1 },
> +	{ 1,  2 },
> +	{ 2,  4 },
> +	{ 3,  8 },
> +	{ 4,  16 },
> +	{ /* sentinel */ }
> +};
> +
> +static struct clk_regmap a9_gp0_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = GP0PLL_CTRL0,
> +		.shift = 20,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "gp0_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_gp0_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +static struct clk_fixed_factor a9_hifi0_in_div2_div = {
> +	.mult = 1,
> +	.div = 2,
> +	.hw.init = &(struct clk_init_data){
> +		.name = "hifi0_in_div2_div",
> +		.ops = &clk_fixed_factor_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_hifi0_in_div2 = {
> +	.data = &(struct clk_regmap_gate_data) {
> +		.offset = HIFIPLL_CTRL0,
> +		.bit_idx = 27,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "hifi0_in_div2",
> +		.ops = &clk_regmap_gate_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_hifi0_in_div2_div.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static const struct reg_sequence a9_hifi0_pll_init_regs[] = {
> +	{ .reg = HIFIPLL_CTRL0, .def = 0x00010000 },
> +	{ .reg = HIFIPLL_CTRL1, .def = 0x11480000 },
> +	{ .reg = HIFIPLL_CTRL2, .def = 0x1219b010 },
> +	{ .reg = HIFIPLL_CTRL3, .def = 0x00008010 }
> +};
> +
> +static struct clk_regmap a9_hifi0_pll_dco = {
> +	.data = &(struct meson_clk_pll_data) {
> +		.en = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 28,
> +			.width   = 1,
> +		},
> +		.m = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 0,
> +			.width   = 9,
> +		},
> +		.n = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 12,
> +			.width   = 3,
> +		},
> +		.frac = {
> +			.reg_off = HIFIPLL_CTRL1,
> +			.shift   = 0,
> +			.width   = 17,
> +		},
> +		.l = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 31,
> +			.width   = 1,
> +		},
> +		.rst = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 29,
> +			.width   = 1,
> +		},
> +		.l_detect = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 30,
> +			.width   = 1,
> +		},
> +		.range = &a9_pll_mult_range,
> +		.init_regs = a9_hifi0_pll_init_regs,
> +		.init_count = ARRAY_SIZE(a9_hifi0_pll_init_regs),
> +		.frac_max = 100000,
> +		.flags = CLK_MESON_PLL_RST_ACTIVE_LOW |
> +			 CLK_MESON_PLL_N_POWER_OF_TWO |
> +			 CLK_MESON_PLL_L_DETECT_ACTIVE_HIGH,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "hifi0_pll_dco",
> +		.ops = &meson_clk_pll_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_hifi0_in_div2.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_hifi0_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = HIFIPLL_CTRL0,
> +		.shift = 20,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "hifi0_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_hifi0_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +static struct clk_fixed_factor a9_hifi1_in_div2_div = {
> +	.mult = 1,
> +	.div = 2,
> +	.hw.init = &(struct clk_init_data){
> +		.name = "hifi1_in_div2_div",
> +		.ops = &clk_fixed_factor_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_hifi1_in_div2 = {
> +	.data = &(struct clk_regmap_gate_data) {
> +		.offset = HIFIPLL_CTRL0,
> +		.bit_idx = 27,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "hifi1_in_div2",
> +		.ops = &clk_regmap_gate_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_hifi1_in_div2_div.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static const struct reg_sequence a9_hifi1_pll_init_regs[] = {
> +	{ .reg = HIFIPLL_CTRL0, .def = 0x00010000 },
> +	{ .reg = HIFIPLL_CTRL1, .def = 0x11480000 },
> +	{ .reg = HIFIPLL_CTRL2, .def = 0x1219b011 },
> +	{ .reg = HIFIPLL_CTRL3, .def = 0x00008010 }
> +};
> +
> +static struct clk_regmap a9_hifi1_pll_dco = {
> +	.data = &(struct meson_clk_pll_data) {
> +		.en = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 28,
> +			.width   = 1,
> +		},
> +		.m = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 0,
> +			.width   = 9,
> +		},
> +		.n = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 12,
> +			.width   = 3,
> +		},
> +		.frac = {
> +			.reg_off = HIFIPLL_CTRL1,
> +			.shift   = 0,
> +			.width   = 17,
> +		},
> +		.l = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 31,
> +			.width   = 1,
> +		},
> +		.rst = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 29,
> +			.width   = 1,
> +		},
> +		.l_detect = {
> +			.reg_off = HIFIPLL_CTRL0,
> +			.shift   = 30,
> +			.width   = 1,
> +		},
> +		.range = &a9_pll_mult_range,
> +		.init_regs = a9_hifi1_pll_init_regs,
> +		.init_count = ARRAY_SIZE(a9_hifi1_pll_init_regs),
> +		.frac_max = 100000,
> +		.flags = CLK_MESON_PLL_RST_ACTIVE_LOW |
> +			 CLK_MESON_PLL_N_POWER_OF_TWO |
> +			 CLK_MESON_PLL_L_DETECT_ACTIVE_HIGH,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "hifi1_pll_dco",
> +		.ops = &meson_clk_pll_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_hifi1_in_div2.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_hifi1_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = HIFIPLL_CTRL0,
> +		.shift = 20,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "hifi1_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_hifi1_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +/*
> + * Unlike GP0 and HIFI PLLs, the input divider 2 of MCLK PLL is
> + * enabled by default and has no enable control bit.
> + */
> +static struct clk_fixed_factor a9_mclk0_in_div2 = {
> +	.mult = 1,
> +	.div = 2,
> +	.hw.init = &(struct clk_init_data){
> +		.name = "mclk0_in_div2_div",
> +		.ops = &clk_fixed_factor_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static const struct reg_sequence a9_mclk0_pll_init_regs[] = {
> +	{ .reg = MCLKPLL_CTRL1, .def = 0x00422000 },
> +	{ .reg = MCLKPLL_CTRL2, .def = 0x60000100 },
> +	{ .reg = MCLKPLL_CTRL3, .def = 0x02000200 },
> +	{ .reg = MCLKPLL_CTRL4, .def = 0xd616d616 }
> +};
> +
> +static struct clk_regmap a9_mclk0_pll_dco = {
> +	.data = &(struct meson_clk_pll_data) {
> +		.en = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 28,
> +			.width   = 1,
> +		},
> +		.m = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 0,
> +			.width   = 9,
> +		},
> +		.n = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 12,
> +			.width   = 3,
> +		},
> +		.l = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 31,
> +			.width   = 1,
> +		},
> +		.rst = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 29,
> +			.width   = 1,
> +		},
> +		.l_detect = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 30,
> +			.width   = 1,
> +		},
> +		.range = &a9_pll_mult_range,
> +		.init_regs = a9_mclk0_pll_init_regs,
> +		.init_count = ARRAY_SIZE(a9_mclk0_pll_init_regs),
> +		.flags = CLK_MESON_PLL_RST_ACTIVE_LOW |
> +			 CLK_MESON_PLL_N_POWER_OF_TWO |
> +			 CLK_MESON_PLL_L_DETECT_ACTIVE_HIGH,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk0_pll_dco",
> +		.ops = &meson_clk_pll_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk0_in_div2.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk0_0_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 0,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk0_0_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk0_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk0_0_pre = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 3,
> +		.width = 5,
> +		.flags = CLK_DIVIDER_MAX_AT_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk0_0_pre",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk0_0_pll.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +static const struct clk_parent_data a9_mclk0_0_parents[] = {
> +	{ .hw = &a9_mclk0_0_pre.hw },
> +	{ .fw_name = "in0" },
> +	{ .fw_name = "in1" },
> +	{ .fw_name = "in2" }
> +};
> +
> +static A9_COMP_SEL(mclk0_0, MCLKPLL_CTRL3, 12, 0x3, a9_mclk0_0_parents);
> +static A9_COMP_DIV(mclk0_0, MCLKPLL_CTRL3, 10, 1);
> +static A9_COMP_GATE(mclk0_0, MCLKPLL_CTRL3, 8);
> +
> +static struct clk_regmap a9_mclk0_1_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 16,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk0_1_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk0_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk0_1_pre = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 19,
> +		.width = 5,
> +		.flags = CLK_DIVIDER_MAX_AT_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk0_1_pre",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk0_1_pll.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +static const struct clk_parent_data a9_mclk0_1_parents[] = {
> +	{ .hw = &a9_mclk0_1_pre.hw },
> +	{ .fw_name = "in0" },
> +	{ .fw_name = "in1" },
> +	{ .fw_name = "in2" }
> +};
> +
> +static A9_COMP_SEL(mclk0_1, MCLKPLL_CTRL3, 28, 0x3, a9_mclk0_1_parents);
> +static A9_COMP_DIV(mclk0_1, MCLKPLL_CTRL3, 26, 1);
> +static A9_COMP_GATE(mclk0_1, MCLKPLL_CTRL3, 24);
> +
> +static struct clk_fixed_factor a9_mclk1_in_div2 = {
> +	.mult = 1,
> +	.div = 2,
> +	.hw.init = &(struct clk_init_data){
> +		.name = "mclk1_in_div2",
> +		.ops = &clk_fixed_factor_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk1_pll_dco = {
> +	.data = &(struct meson_clk_pll_data) {
> +		.en = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 28,
> +			.width   = 1,
> +		},
> +		.m = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 0,
> +			.width   = 9,
> +		},
> +		.n = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 12,
> +			.width   = 3,
> +		},
> +		.l = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 31,
> +			.width   = 1,
> +		},
> +		.rst = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 29,
> +			.width   = 1,
> +		},
> +		.l_detect = {
> +			.reg_off = MCLKPLL_CTRL0,
> +			.shift   = 30,
> +			.width   = 1,
> +		},
> +		.range = &a9_pll_mult_range,
> +		.init_regs = a9_mclk0_pll_init_regs,
> +		.init_count = ARRAY_SIZE(a9_mclk0_pll_init_regs),
> +		.flags = CLK_MESON_PLL_RST_ACTIVE_LOW |
> +			 CLK_MESON_PLL_N_POWER_OF_TWO |
> +			 CLK_MESON_PLL_L_DETECT_ACTIVE_HIGH,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk1_pll_dco",
> +		.ops = &meson_clk_pll_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk1_in_div2.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk1_0_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 0,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk1_0_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk1_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk1_0_pre = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 3,
> +		.width = 5,
> +		.flags = CLK_DIVIDER_MAX_AT_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk1_0_pre",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk1_0_pll.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +static const struct clk_parent_data a9_mclk1_0_parents[] = {
> +	{ .hw = &a9_mclk1_0_pre.hw },
> +	{ .fw_name = "in0" },
> +	{ .fw_name = "in1" },
> +	{ .fw_name = "in2" }
> +};
> +
> +static A9_COMP_SEL(mclk1_0, MCLKPLL_CTRL3, 12, 0x3, a9_mclk1_0_parents);
> +static A9_COMP_DIV(mclk1_0, MCLKPLL_CTRL3, 10, 1);
> +static A9_COMP_GATE(mclk1_0, MCLKPLL_CTRL3, 8);
> +
> +static struct clk_regmap a9_mclk1_1_pll = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 16,
> +		.width = 3,
> +		.table = a9_pll_od_table,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk1_1_pll",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk1_pll_dco.hw
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
> +static struct clk_regmap a9_mclk1_1_pre = {
> +	.data = &(struct clk_regmap_div_data) {
> +		.offset = MCLKPLL_CTRL3,
> +		.shift = 19,
> +		.width = 5,
> +		.flags = CLK_DIVIDER_MAX_AT_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "mclk1_1_pre",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&a9_mclk1_1_pll.hw
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +	},
> +};
> +
> +static const struct clk_parent_data a9_mclk1_1_parents[] = {
> +	{ .hw = &a9_mclk1_1_pre.hw },
> +	{ .fw_name = "in0" },
> +	{ .fw_name = "in1" },
> +	{ .fw_name = "in2" }
> +};
> +
> +static A9_COMP_SEL(mclk1_1, MCLKPLL_CTRL3, 28, 0x3, a9_mclk1_1_parents);
> +static A9_COMP_DIV(mclk1_1, MCLKPLL_CTRL3, 26, 1);
> +static A9_COMP_GATE(mclk1_1, MCLKPLL_CTRL3, 24);
> +
> +static struct clk_hw *a9_gp0_hw_clks[] = {
> +	[CLKID_GP0_IN_DIV2_DIV]		= &a9_gp0_in_div2_div.hw,
> +	[CLKID_GP0_IN_DIV2]		= &a9_gp0_in_div2.hw,
> +	[CLKID_GP0_PLL_DCO]		= &a9_gp0_pll_dco.hw,
> +	[CLKID_GP0_PLL]			= &a9_gp0_pll.hw,
> +};
> +
> +static struct clk_hw *a9_hifi0_hw_clks[] = {
> +	[CLKID_HIFI0_IN_DIV2_DIV]	= &a9_hifi0_in_div2_div.hw,
> +	[CLKID_HIFI0_IN_DIV2]		= &a9_hifi0_in_div2.hw,
> +	[CLKID_HIFI0_PLL_DCO]		= &a9_hifi0_pll_dco.hw,
> +	[CLKID_HIFI0_PLL]		= &a9_hifi0_pll.hw,
> +};
> +
> +static struct clk_hw *a9_hifi1_hw_clks[] = {
> +	[CLKID_HIFI1_IN_DIV2_DIV]	= &a9_hifi1_in_div2_div.hw,
> +	[CLKID_HIFI1_IN_DIV2]		= &a9_hifi1_in_div2.hw,
> +	[CLKID_HIFI1_PLL_DCO]		= &a9_hifi1_pll_dco.hw,
> +	[CLKID_HIFI1_PLL]		= &a9_hifi1_pll.hw,
> +};
> +
> +static struct clk_hw *a9_mclk0_hw_clks[] = {
> +	[CLKID_MCLK0_IN_DIV2]		= &a9_mclk0_in_div2.hw,
> +	[CLKID_MCLK0_PLL_DCO]		= &a9_mclk0_pll_dco.hw,
> +	[CLKID_MCLK0_0_PLL]		= &a9_mclk0_0_pll.hw,
> +	[CLKID_MCLK0_0_PRE]		= &a9_mclk0_0_pre.hw,
> +	[CLKID_MCLK0_0_SEL]		= &a9_mclk0_0_sel.hw,
> +	[CLKID_MCLK0_0_DIV]		= &a9_mclk0_0_div.hw,
> +	[CLKID_MCLK0_0]			= &a9_mclk0_0.hw,
> +	[CLKID_MCLK0_1_PLL]		= &a9_mclk0_1_pll.hw,
> +	[CLKID_MCLK0_1_PRE]		= &a9_mclk0_1_pre.hw,
> +	[CLKID_MCLK0_1_SEL]		= &a9_mclk0_1_sel.hw,
> +	[CLKID_MCLK0_1_DIV]		= &a9_mclk0_1_div.hw,
> +	[CLKID_MCLK0_1]			= &a9_mclk0_1.hw,
> +};
> +
> +static struct clk_hw *a9_mclk1_hw_clks[] = {
> +	[CLKID_MCLK1_IN_DIV2]		= &a9_mclk1_in_div2.hw,
> +	[CLKID_MCLK1_PLL_DCO]		= &a9_mclk1_pll_dco.hw,
> +	[CLKID_MCLK1_0_PLL]		= &a9_mclk1_0_pll.hw,
> +	[CLKID_MCLK1_0_PRE]		= &a9_mclk1_0_pre.hw,
> +	[CLKID_MCLK1_0_SEL]		= &a9_mclk1_0_sel.hw,
> +	[CLKID_MCLK1_0_DIV]		= &a9_mclk1_0_div.hw,
> +	[CLKID_MCLK1_0]			= &a9_mclk1_0.hw,
> +	[CLKID_MCLK1_1_PLL]		= &a9_mclk1_1_pll.hw,
> +	[CLKID_MCLK1_1_PRE]		= &a9_mclk1_1_pre.hw,
> +	[CLKID_MCLK1_1_SEL]		= &a9_mclk1_1_sel.hw,
> +	[CLKID_MCLK1_1_DIV]		= &a9_mclk1_1_div.hw,
> +	[CLKID_MCLK1_1]			= &a9_mclk1_1.hw,
> +};
> +
> +static const struct meson_clkc_data a9_gp0_data = {
> +	.hw_clks = {
> +		.hws = a9_gp0_hw_clks,
> +		.num = ARRAY_SIZE(a9_gp0_hw_clks),
> +	},
> +};
> +
> +static const struct meson_clkc_data a9_hifi0_data = {
> +	.hw_clks = {
> +		.hws = a9_hifi0_hw_clks,
> +		.num = ARRAY_SIZE(a9_hifi0_hw_clks),
> +	},
> +};
> +
> +static const struct meson_clkc_data a9_hifi1_data = {
> +	.hw_clks = {
> +		.hws = a9_hifi1_hw_clks,
> +		.num = ARRAY_SIZE(a9_hifi1_hw_clks),
> +	},
> +};
> +
> +static const struct meson_clkc_data a9_mclk0_data = {
> +	.hw_clks = {
> +		.hws = a9_mclk0_hw_clks,
> +		.num = ARRAY_SIZE(a9_mclk0_hw_clks),
> +	},
> +};
> +
> +static const struct meson_clkc_data a9_mclk1_data = {
> +	.hw_clks = {
> +		.hws = a9_mclk1_hw_clks,
> +		.num = ARRAY_SIZE(a9_mclk1_hw_clks),
> +	},
> +};
> +
> +static const struct of_device_id a9_pll_clkc_match_table[] = {
> +	{ .compatible = "amlogic,a9-gp0-pll",	.data = &a9_gp0_data, },
> +	{ .compatible = "amlogic,a9-hifi0-pll",	.data = &a9_hifi0_data, },
> +	{ .compatible = "amlogic,a9-hifi1-pll",	.data = &a9_hifi1_data, },
> +	{ .compatible = "amlogic,a9-mclk0-pll",	.data = &a9_mclk0_data, },
> +	{ .compatible = "amlogic,a9-mclk1-pll", .data = &a9_mclk1_data, },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, a9_pll_clkc_match_table);
> +
> +static struct platform_driver a9_pll_clkc_driver = {
> +	.probe		= meson_clkc_mmio_probe,
> +	.driver		= {
> +		.name	= "a9-pll-clkc",
> +		.of_match_table = a9_pll_clkc_match_table,
> +	},
> +};
> +module_platform_driver(a9_pll_clkc_driver);
> +
> +MODULE_DESCRIPTION("Amlogic A9 PLL Clock Controller Driver");
> +MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("CLK_MESON");
> 
> -- 
> 2.47.1
> 
> 



^ permalink raw reply

* Re: [PATCH 07/10] clk: amlogic: Support POWER_OF_TWO for PLL pre-divider
From: Brian Masney @ 2026-05-11 15:23 UTC (permalink / raw)
  To: jian.hu
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Neil Armstrong, Jerome Brunet, Xianwei Zhao,
	Kevin Hilman, Martin Blumenstingl, linux-kernel, linux-clk,
	devicetree, linux-amlogic, linux-arm-kernel
In-Reply-To: <20260511-b4-a9_clk-v1-7-41cb4071b7c9@amlogic.com>

On Mon, May 11, 2026 at 08:47:29PM +0800, Jian Hu via B4 Relay wrote:
> From: Jian Hu <jian.hu@amlogic.com>
> 
> The A9 PLL pre-divider uses a division factor of 2^n to ensure a clock
> duty cycle of 50% after predivision.
> 
> Add flag 'CLK_MESON_PLL_N_POWER_OF_TWO' to indicate that the PLL
> pre-divider division factor is 2^n.
> 
> Signed-off-by: Jian Hu <jian.hu@amlogic.com>

Reviewed-by: Brian Masney <bmasney@redhat.com>



^ permalink raw reply

* Re: [PATCH 06/10] clk: amlogic: PLL reset signal supports active-low configuration
From: Brian Masney @ 2026-05-11 15:21 UTC (permalink / raw)
  To: jian.hu
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Neil Armstrong, Jerome Brunet, Xianwei Zhao,
	Kevin Hilman, Martin Blumenstingl, linux-kernel, linux-clk,
	devicetree, linux-amlogic, linux-arm-kernel
In-Reply-To: <20260511-b4-a9_clk-v1-6-41cb4071b7c9@amlogic.com>

On Mon, May 11, 2026 at 08:47:28PM +0800, Jian Hu via B4 Relay wrote:
> From: Jian Hu <jian.hu@amlogic.com>
> 
> In the A9 design, the PLL reset signal is configured as active-low.
> 
> Add the flag 'CLK_MESON_PLL_RST_N' to indicate that the PLL reset signal
> is active-low.

This flag isn't in the patch. I assume that you mean
CLK_MESON_PLL_RST_ACTIVE_LOW?

Brian

> 
> Signed-off-by: Jian Hu <jian.hu@amlogic.com>
> ---
>  drivers/clk/meson/clk-pll.c | 42 +++++++++++++++++++++++++++++++-----------
>  drivers/clk/meson/clk-pll.h |  2 ++
>  2 files changed, 33 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
> index 5a0bd75f85a9..8568ad6ba7b6 100644
> --- a/drivers/clk/meson/clk-pll.c
> +++ b/drivers/clk/meson/clk-pll.c
> @@ -295,10 +295,14 @@ static int meson_clk_pll_is_enabled(struct clk_hw *hw)
>  {
>  	struct clk_regmap *clk = to_clk_regmap(hw);
>  	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
> +	unsigned int rst;
>  
> -	if (MESON_PARM_APPLICABLE(&pll->rst) &&
> -	    meson_parm_read(clk->map, &pll->rst))
> -		return 0;
> +	if (MESON_PARM_APPLICABLE(&pll->rst)) {
> +		rst = meson_parm_read(clk->map, &pll->rst);
> +		if ((rst && !(pll->flags & CLK_MESON_PLL_RST_ACTIVE_LOW)) ||
> +		    (!rst && (pll->flags & CLK_MESON_PLL_RST_ACTIVE_LOW)))
> +			return 0;
> +	}
>  
>  	if (!meson_parm_read(clk->map, &pll->en) ||
>  	    !meson_parm_read(clk->map, &pll->l))
> @@ -326,14 +330,22 @@ static int meson_clk_pll_init(struct clk_hw *hw)
>  		return 0;
>  
>  	if (pll->init_count) {
> -		if (MESON_PARM_APPLICABLE(&pll->rst))
> -			meson_parm_write(clk->map, &pll->rst, 1);
> +		if (MESON_PARM_APPLICABLE(&pll->rst)) {
> +			if (pll->flags & CLK_MESON_PLL_RST_ACTIVE_LOW)
> +				meson_parm_write(clk->map, &pll->rst, 0);
> +			else
> +				meson_parm_write(clk->map, &pll->rst, 1);
> +		}
>  
>  		regmap_multi_reg_write(clk->map, pll->init_regs,
>  				       pll->init_count);
>  
> -		if (MESON_PARM_APPLICABLE(&pll->rst))
> -			meson_parm_write(clk->map, &pll->rst, 0);
> +		if (MESON_PARM_APPLICABLE(&pll->rst)) {
> +			if (pll->flags & CLK_MESON_PLL_RST_ACTIVE_LOW)
> +				meson_parm_write(clk->map, &pll->rst, 1);
> +			else
> +				meson_parm_write(clk->map, &pll->rst, 0);
> +		}
>  	}
>  
>  	return 0;
> @@ -363,15 +375,23 @@ static int meson_clk_pll_enable(struct clk_hw *hw)
>  		return 0;
>  
>  	/* Make sure the pll is in reset */
> -	if (MESON_PARM_APPLICABLE(&pll->rst))
> -		meson_parm_write(clk->map, &pll->rst, 1);
> +	if (MESON_PARM_APPLICABLE(&pll->rst)) {
> +		if (pll->flags & CLK_MESON_PLL_RST_ACTIVE_LOW)
> +			meson_parm_write(clk->map, &pll->rst, 0);
> +		else
> +			meson_parm_write(clk->map, &pll->rst, 1);
> +	}
>  
>  	/* Enable the pll */
>  	meson_parm_write(clk->map, &pll->en, 1);
>  
>  	/* Take the pll out reset */
> -	if (MESON_PARM_APPLICABLE(&pll->rst))
> -		meson_parm_write(clk->map, &pll->rst, 0);
> +	if (MESON_PARM_APPLICABLE(&pll->rst)) {
> +		if (pll->flags & CLK_MESON_PLL_RST_ACTIVE_LOW)
> +			meson_parm_write(clk->map, &pll->rst, 1);
> +		else
> +			meson_parm_write(clk->map, &pll->rst, 0);
> +	}
>  
>  	/*
>  	 * Compared with the previous SoCs, self-adaption current module
> diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h
> index 97b7c70376a3..1be7e6e77631 100644
> --- a/drivers/clk/meson/clk-pll.h
> +++ b/drivers/clk/meson/clk-pll.h
> @@ -31,6 +31,8 @@ struct pll_mult_range {
>  #define CLK_MESON_PLL_NOINIT_ENABLED	BIT(1)
>  /* l_detect signal is active-high */
>  #define CLK_MESON_PLL_L_DETECT_ACTIVE_HIGH	BIT(2)
> +/* rst signal is active-low (Power-on reset) */
> +#define CLK_MESON_PLL_RST_ACTIVE_LOW	BIT(3)
>  
>  struct meson_clk_pll_data {
>  	struct parm en;
> 
> -- 
> 2.47.1
> 
> 



^ permalink raw reply

* Re: [PATCH] serial: atmel: honor CREAD in atmel_set_termios
From: Greg KH @ 2026-05-11 15:17 UTC (permalink / raw)
  To: Rakesh Alasyam
  Cc: richard.genoud, jirislaby, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, linux-serial, linux-kernel, linux-arm-kernel
In-Reply-To: <20260501081317.15477-1-alasyamrakesh77@gmail.com>

On Fri, May 01, 2026 at 01:43:17PM +0530, Rakesh Alasyam wrote:
> Ignore received characters when CREAD is cleared by adding RXRDY
> to ignore_status_mask.
> 
> This replaces an existing TODO in the driver.
> 
> Signed-off-by: Rakesh Alasyam <alasyamrakesh77@gmail.com>
> ---
>  drivers/tty/serial/atmel_serial.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
> index 5d8c1cfc1c60..5b062d8ccabe 100644
> --- a/drivers/tty/serial/atmel_serial.c
> +++ b/drivers/tty/serial/atmel_serial.c
> @@ -2184,8 +2184,8 @@ static void atmel_set_termios(struct uart_port *port,
>  		if (termios->c_iflag & IGNPAR)
>  			port->ignore_status_mask |= ATMEL_US_OVRE;
>  	}
> -	/* TODO: Ignore all characters if CREAD is set.*/
> -
> +	if (!(termios->c_cflag & CREAD))
> +		port->ignore_status_mask |= ATMEL_US_RXRDY;
>  	/* update the per-port timeout */
>  	uart_update_timeout(port, termios->c_cflag, baud);
>  
> -- 
> 2.43.0
> 
> 

Cool, was this tested to work properly?

And a blank line is needed before the comment, right?

thanks,

greg k-h


^ permalink raw reply

* Re: [PATCH] Documentation: KVM: Document guest-visible compatibility expectations
From: Paolo Bonzini @ 2026-05-11 15:14 UTC (permalink / raw)
  To: David Woodhouse, Jonathan Corbet, Shuah Khan, kvm, linux-doc,
	linux-kernel, Sean Christopherson, Jim Mattson
  Cc: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Raghavendra Rao Ananta, Eric Auger,
	Kees Cook, Arnd Bergmann, Nathan Chancellor, linux-arm-kernel,
	kvmarm, linux-kselftest
In-Reply-To: <6856b269d2af706eae397e0cf9c1231f89d9a932.camel@infradead.org>

On 5/11/26 10:57, David Woodhouse wrote:
> From: David Woodhouse <dwmw@amazon.co.uk>
> 
> Document the expectation that KVM maintains guest-visible compatibility
> across host kernel upgrades and rollbacks.  Specifically:
> 
>   - State saved/restored via KVM ioctls must be sufficient for live
>     migration (and live update) between kernel versions.
> 
>   - Where a new kernel introduces a guest-visible change, it provides a
>     mechanism for userspace to select the previous behaviour.
> 
>   - This allows both forward migration (upgrade) and backward migration
>     (rollback) of guests.
> 
> These expectations have been implicitly required on x86 but were not
> explicitly documented. Harmonise the expectations across all of KVM.

One big part of achieving this on x86 is the handling of CPUID.  Despite 
all the mess that KVM_SET_CPUID2 is (and sometimes the underlying 
architecture too, as Jim Mattson would certainly agree), KVM is 
generally able to provide a consistent view of its configuration to the 
guest.  This doesn't quite extend to compatibility across vendors, but 
it does work across processor generations from either Intel or AMD.

I understand that Arm traditionally had much more trouble than x86 with 
vendor-specified behavior that goes beyond the set of architectural 
features, so we may need to tune the expectations.  However, I agree 
with David that this is needed at least as long as the host CPU does not 
change.

Thanks,

Paolo

> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
>   Documentation/virt/kvm/api.rst              | 14 ++++++++++++++
>   Documentation/virt/kvm/review-checklist.rst | 20 ++++++++++++++------
>   2 files changed, 28 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index 269970221797..864f3daa7acb 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -97,6 +97,20 @@ Instead, kvm defines extension identifiers and a facility to query
>   whether a particular extension identifier is available.  If it is, a
>   set of ioctls is available for application use.
>   
> +KVM will ensure that the state that can be saved and restored via the
> +KVM ioctls is sufficient to allow migration of a running guest between
> +host kernels while maintaining full compatibility of the guest-visible
> +device model.  This includes migration to newer kernels (upgrade) and
> +to older kernels (rollback), provided that the older kernel supports
> +the set of features exposed to the guest.  Where a new kernel version
> +introduces a guest-visible change, it will provide a mechanism (such
> +as a capability or a device attribute) that allows userspace to select
> +the previous behaviour.  This serves two purposes: guests migrated
> +from an older kernel can continue to run with their original
> +observable environment, and new guests launched on the newer kernel
> +can be configured to match the feature set of the older kernel, so
> +that they remain migratable to the older kernel in case of rollback.
> +
>   
>   4. API description
>   ==================
> diff --git a/Documentation/virt/kvm/review-checklist.rst b/Documentation/virt/kvm/review-checklist.rst
> index 053f00c50d66..f0fbe1577a90 100644
> --- a/Documentation/virt/kvm/review-checklist.rst
> +++ b/Documentation/virt/kvm/review-checklist.rst
> @@ -18,22 +18,30 @@ Review checklist for kvm patches
>   5.  New features must default to off (userspace should explicitly request them).
>       Performance improvements can and should default to on.
>   
> -6.  New cpu features should be exposed via KVM_GET_SUPPORTED_CPUID2,
> +6.  Guest-visible changes must not break migration compatibility.  A guest
> +    migrated from an older kernel must be able to run with its original
> +    observable environment, and a guest launched on a newer kernel must be
> +    configurable to match the older kernel's feature set for rollback.
> +    Where a change alters guest-visible behaviour, provide a mechanism
> +    (capability, device attribute, etc.) for userspace to select the
> +    previous behaviour.
> +
> +7.  New cpu features should be exposed via KVM_GET_SUPPORTED_CPUID2,
>       or its equivalent for non-x86 architectures
>   
> -7.  The feature should be testable (see below).
> +8.  The feature should be testable (see below).
>   
> -8.  Changes should be vendor neutral when possible.  Changes to common code
> +9.  Changes should be vendor neutral when possible.  Changes to common code
>       are better than duplicating changes to vendor code.
>   
> -9.  Similarly, prefer changes to arch independent code than to arch dependent
> +10. Similarly, prefer changes to arch independent code than to arch dependent
>       code.
>   
> -10. User/kernel interfaces and guest/host interfaces must be 64-bit clean
> +11. User/kernel interfaces and guest/host interfaces must be 64-bit clean
>       (all variables and sizes naturally aligned on 64-bit; use specific types
>       only - u64 rather than ulong).
>   
> -11. New guest visible features must either be documented in a hardware manual
> +12. New guest visible features must either be documented in a hardware manual
>       or be accompanied by documentation.
>   
>   Testing of KVM code



^ permalink raw reply

* [PATCH v3] media: rkisp1: Add support for CAC
From: Barnabás Pőcze @ 2026-05-11 15:09 UTC (permalink / raw)
  To: Dafna Hirschfeld, Laurent Pinchart, Mauro Carvalho Chehab,
	Heiko Stuebner
  Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
	Barnabás Pőcze, Jacopo Mondi

The CAC block implements chromatic aberration correction. Expose it to
userspace using the extensible parameters format. This was tested on the
i.MX8MP platform, but based on available documentation it is also present
in the RK3399 variant (V10). Thus presumably also in later versions,
so no feature flag is introduced.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
changes in v3:
  * send from the corrent email address....

changes in v2:
  * address review comments

v1: https://lore.kernel.org/linux-media/20260323140216.1486161-1-barnabas.pocze@ideasonboard.com/
---
 .../platform/rockchip/rkisp1/rkisp1-params.c  |  68 +++++++++++
 .../platform/rockchip/rkisp1/rkisp1-regs.h    |  15 ++-
 include/uapi/linux/rkisp1-config.h            | 107 +++++++++++++++++-
 3 files changed, 187 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index 6442436a5e42..042b759eba62 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -64,6 +64,7 @@ union rkisp1_ext_params_config {
 	struct rkisp1_ext_params_compand_bls_config compand_bls;
 	struct rkisp1_ext_params_compand_curve_config compand_curve;
 	struct rkisp1_ext_params_wdr_config wdr;
+	struct rkisp1_ext_params_cac_config cac;
 };
 
 enum rkisp1_params_formats {
@@ -1413,6 +1414,47 @@ static void rkisp1_wdr_config(struct rkisp1_params *params,
 				     RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK);
 }
 
+static void rkisp1_cac_config(struct rkisp1_params *params,
+			      const struct rkisp1_cif_isp_cac_config *arg)
+{
+	u32 val;
+
+	/*
+	 * The enable bit is in the same register (RKISP1_CIF_ISP_CAC_CTRL),
+	 * so only set the clipping mode, and do not modify the other bits.
+	 */
+	val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL);
+	val &= ~(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE |
+		 RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE);
+	val |= FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE, arg->h_clip_mode) |
+	       FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE, arg->v_clip_mode);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK, arg->h_count_start) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK, arg->v_count_start);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_COUNT_START, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[0]) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[0]);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_A, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[1]) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[1]);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_B, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[2]) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[2]);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_C, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->x_nf) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->x_ns);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_X_NORM, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->y_nf) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->y_ns);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_Y_NORM, val);
+}
+
 static void
 rkisp1_isp_isr_other_config(struct rkisp1_params *params,
 			    const struct rkisp1_params_cfg *new_params)
@@ -2089,6 +2131,25 @@ static void rkisp1_ext_params_wdr(struct rkisp1_params *params,
 				      RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
 }
 
+static void rkisp1_ext_params_cac(struct rkisp1_params *params,
+				  const union rkisp1_ext_params_config *block)
+{
+	const struct rkisp1_ext_params_cac_config *cac = &block->cac;
+
+	if (cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+					RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
+		return;
+	}
+
+	rkisp1_cac_config(params, &cac->config);
+
+	if ((cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+	    !(params->enabled_blocks & BIT(cac->header.type)))
+		rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+				      RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
+}
+
 typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
 			     const union rkisp1_ext_params_config *config);
 
@@ -2185,6 +2246,10 @@ static const struct rkisp1_ext_params_handler {
 		.handler	= rkisp1_ext_params_wdr,
 		.group		= RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
 	},
+	[RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC] = {
+		.handler	= rkisp1_ext_params_cac,
+		.group		= RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+	},
 };
 
 #define RKISP1_PARAMS_BLOCK_INFO(block, data) \
@@ -2215,6 +2280,7 @@ rkisp1_ext_params_block_types_info[] = {
 	RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve),
 	RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve),
 	RKISP1_PARAMS_BLOCK_INFO(WDR, wdr),
+	RKISP1_PARAMS_BLOCK_INFO(CAC, cac),
 };
 
 static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) ==
@@ -2474,6 +2540,8 @@ void rkisp1_params_disable(struct rkisp1_params *params)
 	rkisp1_ie_enable(params, false);
 	rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE,
 				RKISP1_CIF_ISP_DPF_MODE_EN);
+	rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+				RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
 }
 
 static const struct rkisp1_params_ops rkisp1_v10_params_ops = {
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index fbeb186cde0d..2b842194de8f 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -724,6 +724,17 @@
 #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK		GENMASK(20, 16)
 #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX		16U
 
+/* CAC */
+#define RKISP1_CIF_ISP_CAC_CTRL_ENABLE		BIT(0)
+#define RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE	GENMASK(2, 1)
+#define RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE	BIT(3)
+#define RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK	GENMASK(12, 0)
+#define RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK	GENMASK(28, 16)
+#define RKISP1_CIF_ISP_CAC_RED_MASK		GENMASK(8, 0)
+#define RKISP1_CIF_ISP_CAC_BLUE_MASK		GENMASK(24, 16)
+#define RKISP1_CIF_ISP_CAC_NF_MASK		GENMASK(4, 0)
+#define RKISP1_CIF_ISP_CAC_NS_MASK		GENMASK(19, 16)
+
 /* =================================================================== */
 /*                            CIF Registers                            */
 /* =================================================================== */
@@ -1196,8 +1207,8 @@
 #define RKISP1_CIF_ISP_CAC_A			(RKISP1_CIF_ISP_CAC_BASE + 0x00000008)
 #define RKISP1_CIF_ISP_CAC_B			(RKISP1_CIF_ISP_CAC_BASE + 0x0000000c)
 #define RKISP1_CIF_ISP_CAC_C			(RKISP1_CIF_ISP_CAC_BASE + 0x00000010)
-#define RKISP1_CIF_ISP_X_NORM			(RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
-#define RKISP1_CIF_ISP_Y_NORM			(RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_CAC_X_NORM		(RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_CAC_Y_NORM		(RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
 
 #define RKISP1_CIF_ISP_EXP_BASE			0x00002600
 #define RKISP1_CIF_ISP_EXP_CTRL			(RKISP1_CIF_ISP_EXP_BASE + 0x00000000)
diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h
index b2d2a71f7baf..c6ba49621726 100644
--- a/include/uapi/linux/rkisp1-config.h
+++ b/include/uapi/linux/rkisp1-config.h
@@ -967,6 +967,92 @@ struct rkisp1_cif_isp_wdr_config {
 	__u8 use_iref;
 };
 
+/*
+ * enum rkisp1_cif_isp_cac_h_clip_mode - horizontal clipping mode
+ *
+ * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX: +/- 4 pixels
+ * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX: +/- 4/5 pixels depending on bayer position
+ */
+enum rkisp1_cif_isp_cac_h_clip_mode {
+	RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX = 0,
+	RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX = 1,
+};
+
+/**
+ * enum rkisp1_cif_isp_cac_v_clip_mode - vertical clipping mode
+ *
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX: +/- 2 pixels
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX: +/- 3 pixels
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX: +/- 3/4 pixels depending on bayer position
+ */
+enum rkisp1_cif_isp_cac_v_clip_mode {
+	RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX = 0,
+	RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX = 1,
+	RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX = 2,
+};
+
+/**
+ * struct rkisp1_cif_isp_cac_config - chromatic aberration correction configuration
+ *
+ * The correction is carried out by shifting the red and blue pixels relative
+ * to the green ones, depending on the distance from the optical center:
+ *
+ * @h_count_start: horizontal coordinate of the optical center (13-bit unsigned integer; [1,8191])
+ * @v_count_start: vertical coordinate of the optical center (13-bit unsigned integer; [1,8191])
+ *
+ * For each pixel, the x/y distances from the optical center are calculated and
+ * then transformed into the [0,255] range based on the following formula:
+ *
+ *   (((d << 4) >> ns) * nf) >> 5
+ *
+ * where `d` is the distance, `ns` and `nf` are the normalization parameters:
+ *
+ * @x_nf: horizontal normalization scale parameter (5-bit unsigned integer; [0,31])
+ * @x_ns: horizontal normalization shift parameter (4-bit unsigned integer; [0,15])
+ *
+ * @y_nf: vertical normalization scale parameter (5-bit unsigned integer; [0,31])
+ * @y_ns: vertical normalization shift parameter (4-bit unsigned integer; [0,15])
+ *
+ * These parameters should be chosen based on the image resolution, the position
+ * of the optical center, and the shape of pixels, so that no normalized distance
+ * is larger than 255. If the pixels have square shape, the two sets of parameters
+ * should be equal.
+ *
+ * The actual amount of correction is calculated with a third degree polynomial:
+ *
+ *   c[0] * r + c[1] * r^2 + c[2] * r^3
+ *
+ * where `c` is the set of coefficients for the given color, and `r` is distance:
+ *
+ * @red: red coefficients (5.4 two's complement; [-16,15.9375])
+ * @blue: blue coefficients (5.4 two's complement; [-16,15.9375])
+ *
+ * Finally, the amount is clipped as requested:
+ *
+ * @h_clip_mode: maximum horizontal shift (from enum rkisp1_cif_isp_cac_h_clip_mode)
+ * @v_clip_mode: maximum vertical shift (from enum rkisp1_cif_isp_cac_v_clip_mode)
+ *
+ * A positive result will shift away from the optical center, while a negative
+ * one will shift towards the optical center. In the latter case, the pixel
+ * values at the edges are duplicated.
+ */
+struct rkisp1_cif_isp_cac_config {
+	__u8 h_clip_mode;
+	__u8 v_clip_mode;
+
+	__u16 h_count_start;
+	__u16 v_count_start;
+
+	__u16 red[3];
+	__u16 blue[3];
+
+	__u8 x_nf;
+	__u8 x_ns;
+
+	__u8 y_nf;
+	__u8 y_ns;
+};
+
 /*---------- PART2: Measurement Statistics ------------*/
 
 /**
@@ -1138,6 +1224,7 @@ struct rkisp1_stat_buffer {
  * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve
  * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve
  * @RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR: Wide dynamic range
+ * @RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC: Chromatic aberration correction
  */
 enum rkisp1_ext_params_block_type {
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS,
@@ -1161,6 +1248,7 @@ enum rkisp1_ext_params_block_type {
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND,
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS,
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR,
+	RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC,
 };
 
 /* For backward compatibility */
@@ -1507,6 +1595,22 @@ struct rkisp1_ext_params_wdr_config {
 	struct rkisp1_cif_isp_wdr_config config;
 } __attribute__((aligned(8)));
 
+/**
+ * struct rkisp1_ext_params_cac_config - RkISP1 extensible params CAC config
+ *
+ * RkISP1 extensible parameters CAC block.
+ * Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC`.
+ *
+ * @header: The RkISP1 extensible parameters header, see
+ *	    :c:type:`rkisp1_ext_params_block_header`
+ * @config: CAC configuration, see
+ *	    :c:type:`rkisp1_cif_isp_cac_config`
+ */
+struct rkisp1_ext_params_cac_config {
+	struct rkisp1_ext_params_block_header header;
+	struct rkisp1_cif_isp_cac_config config;
+} __attribute__((aligned(8)));
+
 /*
  * The rkisp1_ext_params_compand_curve_config structure is counted twice as it
  * is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types.
@@ -1532,7 +1636,8 @@ struct rkisp1_ext_params_wdr_config {
 	sizeof(struct rkisp1_ext_params_compand_bls_config)		+\
 	sizeof(struct rkisp1_ext_params_compand_curve_config)		+\
 	sizeof(struct rkisp1_ext_params_compand_curve_config)		+\
-	sizeof(struct rkisp1_ext_params_wdr_config))
+	sizeof(struct rkisp1_ext_params_wdr_config)			+\
+	sizeof(struct rkisp1_ext_params_cac_config))
 
 /**
  * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version
-- 
2.54.0



^ permalink raw reply related

* [PATCH v2] media: rkisp1: Add support for CAC
From: Barnabás Pőcze @ 2026-05-11 15:04 UTC (permalink / raw)
  To: Dafna Hirschfeld, Laurent Pinchart, Mauro Carvalho Chehab,
	Heiko Stuebner
  Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
	Barnabás Pőcze, Jacopo Mondi

From: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>

The CAC block implements chromatic aberration correction. Expose it to
userspace using the extensible parameters format. This was tested on the
i.MX8MP platform, but based on available documentation it is also present
in the RK3399 variant (V10). Thus presumably also in later versions,
so no feature flag is introduced.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
changes in v2:
  * address review comments

v1: https://lore.kernel.org/linux-media/20260323140216.1486161-1-barnabas.pocze@ideasonboard.com/
---
 .../platform/rockchip/rkisp1/rkisp1-params.c  |  68 +++++++++++
 .../platform/rockchip/rkisp1/rkisp1-regs.h    |  15 ++-
 include/uapi/linux/rkisp1-config.h            | 107 +++++++++++++++++-
 3 files changed, 187 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index 6442436a5e42..042b759eba62 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -64,6 +64,7 @@ union rkisp1_ext_params_config {
 	struct rkisp1_ext_params_compand_bls_config compand_bls;
 	struct rkisp1_ext_params_compand_curve_config compand_curve;
 	struct rkisp1_ext_params_wdr_config wdr;
+	struct rkisp1_ext_params_cac_config cac;
 };
 
 enum rkisp1_params_formats {
@@ -1413,6 +1414,47 @@ static void rkisp1_wdr_config(struct rkisp1_params *params,
 				     RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK);
 }
 
+static void rkisp1_cac_config(struct rkisp1_params *params,
+			      const struct rkisp1_cif_isp_cac_config *arg)
+{
+	u32 val;
+
+	/*
+	 * The enable bit is in the same register (RKISP1_CIF_ISP_CAC_CTRL),
+	 * so only set the clipping mode, and do not modify the other bits.
+	 */
+	val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL);
+	val &= ~(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE |
+		 RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE);
+	val |= FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE, arg->h_clip_mode) |
+	       FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE, arg->v_clip_mode);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK, arg->h_count_start) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK, arg->v_count_start);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_COUNT_START, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[0]) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[0]);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_A, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[1]) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[1]);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_B, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[2]) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[2]);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_C, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->x_nf) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->x_ns);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_X_NORM, val);
+
+	val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->y_nf) |
+	      FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->y_ns);
+	rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_Y_NORM, val);
+}
+
 static void
 rkisp1_isp_isr_other_config(struct rkisp1_params *params,
 			    const struct rkisp1_params_cfg *new_params)
@@ -2089,6 +2131,25 @@ static void rkisp1_ext_params_wdr(struct rkisp1_params *params,
 				      RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
 }
 
+static void rkisp1_ext_params_cac(struct rkisp1_params *params,
+				  const union rkisp1_ext_params_config *block)
+{
+	const struct rkisp1_ext_params_cac_config *cac = &block->cac;
+
+	if (cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+					RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
+		return;
+	}
+
+	rkisp1_cac_config(params, &cac->config);
+
+	if ((cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+	    !(params->enabled_blocks & BIT(cac->header.type)))
+		rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+				      RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
+}
+
 typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
 			     const union rkisp1_ext_params_config *config);
 
@@ -2185,6 +2246,10 @@ static const struct rkisp1_ext_params_handler {
 		.handler	= rkisp1_ext_params_wdr,
 		.group		= RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
 	},
+	[RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC] = {
+		.handler	= rkisp1_ext_params_cac,
+		.group		= RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+	},
 };
 
 #define RKISP1_PARAMS_BLOCK_INFO(block, data) \
@@ -2215,6 +2280,7 @@ rkisp1_ext_params_block_types_info[] = {
 	RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve),
 	RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve),
 	RKISP1_PARAMS_BLOCK_INFO(WDR, wdr),
+	RKISP1_PARAMS_BLOCK_INFO(CAC, cac),
 };
 
 static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) ==
@@ -2474,6 +2540,8 @@ void rkisp1_params_disable(struct rkisp1_params *params)
 	rkisp1_ie_enable(params, false);
 	rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE,
 				RKISP1_CIF_ISP_DPF_MODE_EN);
+	rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+				RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
 }
 
 static const struct rkisp1_params_ops rkisp1_v10_params_ops = {
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index fbeb186cde0d..2b842194de8f 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -724,6 +724,17 @@
 #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK		GENMASK(20, 16)
 #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX		16U
 
+/* CAC */
+#define RKISP1_CIF_ISP_CAC_CTRL_ENABLE		BIT(0)
+#define RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE	GENMASK(2, 1)
+#define RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE	BIT(3)
+#define RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK	GENMASK(12, 0)
+#define RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK	GENMASK(28, 16)
+#define RKISP1_CIF_ISP_CAC_RED_MASK		GENMASK(8, 0)
+#define RKISP1_CIF_ISP_CAC_BLUE_MASK		GENMASK(24, 16)
+#define RKISP1_CIF_ISP_CAC_NF_MASK		GENMASK(4, 0)
+#define RKISP1_CIF_ISP_CAC_NS_MASK		GENMASK(19, 16)
+
 /* =================================================================== */
 /*                            CIF Registers                            */
 /* =================================================================== */
@@ -1196,8 +1207,8 @@
 #define RKISP1_CIF_ISP_CAC_A			(RKISP1_CIF_ISP_CAC_BASE + 0x00000008)
 #define RKISP1_CIF_ISP_CAC_B			(RKISP1_CIF_ISP_CAC_BASE + 0x0000000c)
 #define RKISP1_CIF_ISP_CAC_C			(RKISP1_CIF_ISP_CAC_BASE + 0x00000010)
-#define RKISP1_CIF_ISP_X_NORM			(RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
-#define RKISP1_CIF_ISP_Y_NORM			(RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_CAC_X_NORM		(RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_CAC_Y_NORM		(RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
 
 #define RKISP1_CIF_ISP_EXP_BASE			0x00002600
 #define RKISP1_CIF_ISP_EXP_CTRL			(RKISP1_CIF_ISP_EXP_BASE + 0x00000000)
diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h
index b2d2a71f7baf..c6ba49621726 100644
--- a/include/uapi/linux/rkisp1-config.h
+++ b/include/uapi/linux/rkisp1-config.h
@@ -967,6 +967,92 @@ struct rkisp1_cif_isp_wdr_config {
 	__u8 use_iref;
 };
 
+/*
+ * enum rkisp1_cif_isp_cac_h_clip_mode - horizontal clipping mode
+ *
+ * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX: +/- 4 pixels
+ * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX: +/- 4/5 pixels depending on bayer position
+ */
+enum rkisp1_cif_isp_cac_h_clip_mode {
+	RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX = 0,
+	RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX = 1,
+};
+
+/**
+ * enum rkisp1_cif_isp_cac_v_clip_mode - vertical clipping mode
+ *
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX: +/- 2 pixels
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX: +/- 3 pixels
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX: +/- 3/4 pixels depending on bayer position
+ */
+enum rkisp1_cif_isp_cac_v_clip_mode {
+	RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX = 0,
+	RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX = 1,
+	RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX = 2,
+};
+
+/**
+ * struct rkisp1_cif_isp_cac_config - chromatic aberration correction configuration
+ *
+ * The correction is carried out by shifting the red and blue pixels relative
+ * to the green ones, depending on the distance from the optical center:
+ *
+ * @h_count_start: horizontal coordinate of the optical center (13-bit unsigned integer; [1,8191])
+ * @v_count_start: vertical coordinate of the optical center (13-bit unsigned integer; [1,8191])
+ *
+ * For each pixel, the x/y distances from the optical center are calculated and
+ * then transformed into the [0,255] range based on the following formula:
+ *
+ *   (((d << 4) >> ns) * nf) >> 5
+ *
+ * where `d` is the distance, `ns` and `nf` are the normalization parameters:
+ *
+ * @x_nf: horizontal normalization scale parameter (5-bit unsigned integer; [0,31])
+ * @x_ns: horizontal normalization shift parameter (4-bit unsigned integer; [0,15])
+ *
+ * @y_nf: vertical normalization scale parameter (5-bit unsigned integer; [0,31])
+ * @y_ns: vertical normalization shift parameter (4-bit unsigned integer; [0,15])
+ *
+ * These parameters should be chosen based on the image resolution, the position
+ * of the optical center, and the shape of pixels, so that no normalized distance
+ * is larger than 255. If the pixels have square shape, the two sets of parameters
+ * should be equal.
+ *
+ * The actual amount of correction is calculated with a third degree polynomial:
+ *
+ *   c[0] * r + c[1] * r^2 + c[2] * r^3
+ *
+ * where `c` is the set of coefficients for the given color, and `r` is distance:
+ *
+ * @red: red coefficients (5.4 two's complement; [-16,15.9375])
+ * @blue: blue coefficients (5.4 two's complement; [-16,15.9375])
+ *
+ * Finally, the amount is clipped as requested:
+ *
+ * @h_clip_mode: maximum horizontal shift (from enum rkisp1_cif_isp_cac_h_clip_mode)
+ * @v_clip_mode: maximum vertical shift (from enum rkisp1_cif_isp_cac_v_clip_mode)
+ *
+ * A positive result will shift away from the optical center, while a negative
+ * one will shift towards the optical center. In the latter case, the pixel
+ * values at the edges are duplicated.
+ */
+struct rkisp1_cif_isp_cac_config {
+	__u8 h_clip_mode;
+	__u8 v_clip_mode;
+
+	__u16 h_count_start;
+	__u16 v_count_start;
+
+	__u16 red[3];
+	__u16 blue[3];
+
+	__u8 x_nf;
+	__u8 x_ns;
+
+	__u8 y_nf;
+	__u8 y_ns;
+};
+
 /*---------- PART2: Measurement Statistics ------------*/
 
 /**
@@ -1138,6 +1224,7 @@ struct rkisp1_stat_buffer {
  * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve
  * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve
  * @RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR: Wide dynamic range
+ * @RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC: Chromatic aberration correction
  */
 enum rkisp1_ext_params_block_type {
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS,
@@ -1161,6 +1248,7 @@ enum rkisp1_ext_params_block_type {
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND,
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS,
 	RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR,
+	RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC,
 };
 
 /* For backward compatibility */
@@ -1507,6 +1595,22 @@ struct rkisp1_ext_params_wdr_config {
 	struct rkisp1_cif_isp_wdr_config config;
 } __attribute__((aligned(8)));
 
+/**
+ * struct rkisp1_ext_params_cac_config - RkISP1 extensible params CAC config
+ *
+ * RkISP1 extensible parameters CAC block.
+ * Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC`.
+ *
+ * @header: The RkISP1 extensible parameters header, see
+ *	    :c:type:`rkisp1_ext_params_block_header`
+ * @config: CAC configuration, see
+ *	    :c:type:`rkisp1_cif_isp_cac_config`
+ */
+struct rkisp1_ext_params_cac_config {
+	struct rkisp1_ext_params_block_header header;
+	struct rkisp1_cif_isp_cac_config config;
+} __attribute__((aligned(8)));
+
 /*
  * The rkisp1_ext_params_compand_curve_config structure is counted twice as it
  * is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types.
@@ -1532,7 +1636,8 @@ struct rkisp1_ext_params_wdr_config {
 	sizeof(struct rkisp1_ext_params_compand_bls_config)		+\
 	sizeof(struct rkisp1_ext_params_compand_curve_config)		+\
 	sizeof(struct rkisp1_ext_params_compand_curve_config)		+\
-	sizeof(struct rkisp1_ext_params_wdr_config))
+	sizeof(struct rkisp1_ext_params_wdr_config)			+\
+	sizeof(struct rkisp1_ext_params_cac_config))
 
 /**
  * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version
-- 
2.54.0




^ permalink raw reply related

* Re: [PATCH tty v4 0/6] 8250: Add console flow control
From: Greg Kroah-Hartman @ 2026-05-11 14:58 UTC (permalink / raw)
  To: John Ogness
  Cc: Jiri Slaby, Andy Shevchenko, linux-kernel, linux-serial,
	Krzysztof Kozlowski, Alim Akhtar, David S. Miller,
	Ilpo Järvinen, Andy Shevchenko, Thomas Fourier, Kees Cook,
	linux-arm-kernel, linux-samsung-soc, sparclinux, Biju Das,
	Geert Uytterhoeven, Lad Prabhakar, Thierry Bultel,
	Osama Abdelkader, Ingo Molnar, Xin Zhao, Joseph Tilahun,
	Krzysztof Kozlowski, Lukas Wunner, Dr. David Alan Gilbert
In-Reply-To: <20260506121606.5805-1-john.ogness@linutronix.de>

On Wed, May 06, 2026 at 02:21:55PM +0206, John Ogness wrote:
> Hi,
> 
> This is v4 of a series to implement console flow control for the
> 8250 serial driver. v3 is here [0].
> 
> The 8250 driver already has code in place to support console flow
> control. However, there is no way to activate it and it is
> incomplete. This series provides the necessary missing pieces while
> attempting to be as conservative as possible, so as not to introduce
> any side effects into the many 8250 variants or other non-8250 serial
> drivers.

This had some rejections in drivers/tty/serial/sh-sci.c, what
branch/tree did you make this against?

thanks,

greg k-h


^ permalink raw reply

* Re: [PATCH v7 00/20] ARM64 PMU Partitioning
From: James Clark @ 2026-05-11 14:57 UTC (permalink / raw)
  To: Colton Lewis
  Cc: Alexandru Elisei, Paolo Bonzini, Jonathan Corbet, Russell King,
	Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
	Mingwei Zhang, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Mark Rutland, Shuah Khan, Ganapatrao Kulkarni, linux-doc,
	linux-kernel, linux-arm-kernel, kvmarm, linux-perf-users,
	linux-kselftest
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>



On 04/05/2026 10:17 pm, Colton Lewis wrote:
> This series creates a new PMU scheme on ARM, a partitioned PMU that
> allows reserving a subset of counters for more direct guest access,
> significantly reducing overhead. More details, including performance
> benchmarks, can be read in the v1 cover letter linked below.
> 
> An overview of what this series accomplishes was presented at KVM
> Forum 2025. Slides [1] and video [2] are linked below.
> 
> After a few false starts, meeting with Will Deacon and Mark Rutland to
> discuss implementation ideas, and a few more false starts, I finally
> have an implementation of dynamic counter reservation that works
> without disrupting host perf too much. Now the host only loses access
> to the guest counters when a vCPU resides on the CPU.
> 
> The key was creating perf_pmu_resched_update, which behaves exactly
> like perf_pmu_resched except it takes a callback to call in between
> when the perf events are scheduled out and when they are scheduled
> back in. That allows us to update the PMU's available counters when we
> know they are not currently in use without needing to expose private
> perf core functions and triple check they are not being called in a
> way that violates existing assumptions.
> 
> Because this introduces a possibility of perf reschedule during vCPU
> load, I've optimized to only do that operation if there are host
> events occupying the intended guest counters at the time of the load.
> 
> The kernel command line parameter for the driver still exists, but now
> only defines an upper limit of counters the guest might use rather
> than taking those counters from the host permanently.
> 
> v7:
> 
> * Implement dynamic counter reservation as described above. One side
>    effect is the PMUv3 driver now needs much fewer changes to enforce
>    the boundary.
> 
> * Move register accesses out of fast path for non-FGT hardware. The
>    performance impact was negligible and this moves bloat out of the
>    fast path and allows a more reliable design with more code sharing.
> 
> * Make PMCCNTR a special case in the context swap again because trying
>    to access it with PMXEVCNTR is undefined.
> 
> * Fix a bug where kvm_pmu_guest_counter_mask was using & instead of |.
> 
> * Re-expose the dedicated instruction counter to the host since it was
>    decided the guest will not own it.
> 
> * Change the global armv8pmu_reserved_host_counters to
>    armv8pmu_is_partitoned because it was only used in boolean checks.
> 
> * Fix typo in vcpu attribute commit so the spelling of the flag in the
>    commit message matches the code.
> 
> * Rebase to v7.0-rc7
> 
> v6:
> https://lore.kernel.org/kvmarm/20260209221414.2169465-1-coltonlewis@google.com/
> 
> v5:
> https://lore.kernel.org/kvmarm/20251209205121.1871534-1-coltonlewis@google.com/
> 
> v4:
> https://lore.kernel.org/kvmarm/20250714225917.1396543-1-coltonlewis@google.com/
> 
> v3:
> https://lore.kernel.org/kvm/20250626200459.1153955-1-coltonlewis@google.com/
> 
> v2:
> https://lore.kernel.org/kvm/20250620221326.1261128-1-coltonlewis@google.com/
> 
> v1:
> https://lore.kernel.org/kvm/20250602192702.2125115-1-coltonlewis@google.com/
> 
> [1] https://gitlab.com/qemu-project/kvm-forum/-/raw/main/_attachments/2025/Optimizing__itvHkhc.pdf
> [2] https://www.youtube.com/watch?v=YRzZ8jMIA6M&list=PLW3ep1uCIRfxwmllXTOA2txfDWN6vUOHp&index=9
> 
> Colton Lewis (19):
>    arm64: cpufeature: Add cpucap for HPMN0
>    KVM: arm64: Reorganize PMU functions
>    perf: arm_pmuv3: Generalize counter bitmasks
>    perf: arm_pmuv3: Check cntr_mask before using pmccntr
>    perf: arm_pmuv3: Add method to partition the PMU
>    KVM: arm64: Set up FGT for Partitioned PMU
>    KVM: arm64: Add Partitioned PMU register trap handlers
>    KVM: arm64: Set up MDCR_EL2 to handle a Partitioned PMU
>    KVM: arm64: Context swap Partitioned PMU guest registers
>    KVM: arm64: Enforce PMU event filter at vcpu_load()
>    perf: Add perf_pmu_resched_update()
>    KVM: arm64: Apply dynamic guest counter reservations
>    KVM: arm64: Implement lazy PMU context swaps
>    perf: arm_pmuv3: Handle IRQs for Partitioned PMU guest counters
>    KVM: arm64: Detect overflows for the Partitioned PMU
>    KVM: arm64: Add vCPU device attr to partition the PMU
>    KVM: selftests: Add find_bit to KVM library
>    KVM: arm64: selftests: Add test case for Partitioned PMU
>    KVM: arm64: selftests: Relax testing for exceptions when partitioned
> 
> Marc Zyngier (1):
>    KVM: arm64: Reorganize PMU includes
> 
>   arch/arm/include/asm/arm_pmuv3.h              |  18 +
>   arch/arm64/include/asm/arm_pmuv3.h            |  12 +-
>   arch/arm64/include/asm/kvm_host.h             |  17 +-
>   arch/arm64/include/asm/kvm_types.h            |   6 +-
>   arch/arm64/include/uapi/asm/kvm.h             |   2 +
>   arch/arm64/kernel/cpufeature.c                |   8 +
>   arch/arm64/kvm/Makefile                       |   2 +-
>   arch/arm64/kvm/arm.c                          |   2 +
>   arch/arm64/kvm/config.c                       |  41 +-
>   arch/arm64/kvm/debug.c                        |  31 +-
>   arch/arm64/kvm/pmu-direct.c                   | 494 ++++++++++++
>   arch/arm64/kvm/pmu-emul.c                     | 674 +----------------
>   arch/arm64/kvm/pmu.c                          | 701 ++++++++++++++++++
>   arch/arm64/kvm/sys_regs.c                     | 250 ++++++-
>   arch/arm64/tools/cpucaps                      |   1 +
>   arch/arm64/tools/sysreg                       |   6 +-
>   drivers/perf/arm_pmuv3.c                      | 111 ++-
>   include/kvm/arm_pmu.h                         | 110 +++
>   include/linux/perf/arm_pmu.h                  |   3 +
>   include/linux/perf/arm_pmuv3.h                |  14 +-
>   include/linux/perf_event.h                    |   3 +
>   kernel/events/core.c                          |  28 +-
>   tools/testing/selftests/kvm/Makefile.kvm      |   1 +
>   .../selftests/kvm/arm64/vpmu_counter_access.c | 112 ++-
>   tools/testing/selftests/kvm/lib/find_bit.c    |   1 +
>   25 files changed, 1861 insertions(+), 787 deletions(-)
>   create mode 100644 arch/arm64/kvm/pmu-direct.c
>   create mode 100644 tools/testing/selftests/kvm/lib/find_bit.c
> 
> 
> base-commit: 591cd656a1bf5ea94a222af5ef2ee76df029c1d2
> --
> 2.54.0.545.g6539524ca2-goog

I tested it a bit and ran the kselftests and it all seems to be working 
ok. Some of the critical sashiko comments look like they are worth 
looking into though: 
https://sashiko.dev/#/patchset/20260504211813.1804997-1-coltonlewis%40google.com

For example writing to PMCR_EL0.P from EL2 resets the host's counters, 
even if it's KVM doing it after trapping a write from the guest.



^ permalink raw reply

* Re: [PATCH v7 06/20] perf: arm_pmuv3: Add method to partition the PMU
From: James Clark @ 2026-05-11 14:51 UTC (permalink / raw)
  To: Colton Lewis
  Cc: Alexandru Elisei, Paolo Bonzini, Jonathan Corbet, Russell King,
	Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
	Mingwei Zhang, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Mark Rutland, Shuah Khan, Ganapatrao Kulkarni, linux-doc,
	linux-kernel, linux-arm-kernel, kvmarm, linux-perf-users,
	linux-kselftest, kvm
In-Reply-To: <20260504211813.1804997-7-coltonlewis@google.com>



On 04/05/2026 10:17 pm, Colton Lewis wrote:
> For PMUv3, the register field MDCR_EL2.HPMN partitiones the PMU
> counters into two ranges where counters 0..HPMN-1 are accessible by
> EL1 and, if allowed, EL0 while counters HPMN..N are only accessible by
> EL2.
> 
> Create a module parameter reserved_host_counters to reserve a number
> of counters for the host. Counters not reserved for the host may be
> used by a guest VM when the PMU is partitioned.
> 
> Add the function armv8pmu_partition() to check the validity of the
> reservation and record a partition has happened and the maximum
> allowable value for HPMN.
> 
> Due to the difficulty this feature would create for the driver running
> in nVHE mode, partitioning is only allowed in VHE mode. In order to
> support a partitioning on nVHE we'd need to explicitly disable guest
> counters on every exit and reset HPMN to place all counters in the
> first range.
> 
> Signed-off-by: Colton Lewis <coltonlewis@google.com>
> ---
>   arch/arm/include/asm/arm_pmuv3.h   |  4 ++
>   arch/arm64/include/asm/arm_pmuv3.h |  5 ++
>   arch/arm64/kvm/Makefile            |  2 +-
>   arch/arm64/kvm/pmu-direct.c        | 22 +++++++++
>   drivers/perf/arm_pmuv3.c           | 77 ++++++++++++++++++++++++++++--
>   include/kvm/arm_pmu.h              |  8 ++++
>   include/linux/perf/arm_pmu.h       |  2 +
>   7 files changed, 115 insertions(+), 5 deletions(-)
>   create mode 100644 arch/arm64/kvm/pmu-direct.c
> 
> diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
> index 2ec0e5e83fc98..154503f054886 100644
> --- a/arch/arm/include/asm/arm_pmuv3.h
> +++ b/arch/arm/include/asm/arm_pmuv3.h
> @@ -221,6 +221,10 @@ static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr)
>   	return false;
>   }
>   
> +static inline bool has_host_pmu_partition_support(void)
> +{
> +	return false;
> +}
>   static inline bool kvm_set_pmuserenr(u64 val)
>   {
>   	return false;
> diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/arm_pmuv3.h
> index cf2b2212e00a2..27c4d6d47da31 100644
> --- a/arch/arm64/include/asm/arm_pmuv3.h
> +++ b/arch/arm64/include/asm/arm_pmuv3.h
> @@ -171,6 +171,11 @@ static inline bool pmuv3_implemented(int pmuver)
>   		 pmuver == ID_AA64DFR0_EL1_PMUVer_NI);
>   }
>   
> +static inline bool is_pmuv3p1(int pmuver)
> +{
> +	return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P1;
> +}
> +
>   static inline bool is_pmuv3p4(int pmuver)
>   {
>   	return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P4;
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 3ebc0570345cc..baf0f296c0e53 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -26,7 +26,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
>   	 vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \
>   	 vgic/vgic-v5.o
>   
> -kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o pmu.o
> +kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o pmu-direct.o pmu.o
>   kvm-$(CONFIG_ARM64_PTR_AUTH)  += pauth.o
>   kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) += ptdump.o
>   
> diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
> new file mode 100644
> index 0000000000000..74e40e4915416
> --- /dev/null
> +++ b/arch/arm64/kvm/pmu-direct.c
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2025 Google LLC
> + * Author: Colton Lewis <coltonlewis@google.com>
> + */
> +
> +#include <linux/kvm_host.h>
> +
> +#include <asm/arm_pmuv3.h>
> +
> +/**
> + * has_host_pmu_partition_support() - Determine if partitioning is possible
> + *
> + * Partitioning is only supported in VHE mode with PMUv3
> + *
> + * Return: True if partitioning is possible, false otherwise
> + */
> +bool has_host_pmu_partition_support(void)
> +{
> +	return has_vhe() &&
> +		system_supports_pmuv3();
> +}
> diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
> index 7ff3139dda893..6e447227d801f 100644
> --- a/drivers/perf/arm_pmuv3.c
> +++ b/drivers/perf/arm_pmuv3.c
> @@ -42,6 +42,13 @@
>   #define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_ACCESS		0xEC
>   #define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_MISS		0xED
>   
> +static int reserved_host_counters __read_mostly = -1;
> +bool armv8pmu_is_partitioned;
> +
> +module_param(reserved_host_counters, int, 0);
> +MODULE_PARM_DESC(reserved_host_counters,
> +		 "PMU Partition: -1 = No partition; +N = Reserve N counters for the host");
> +
>   /*
>    * ARMv8 Architectural defined events, not all of these may
>    * be supported on any given implementation. Unsupported events will
> @@ -532,6 +539,11 @@ static void armv8pmu_pmcr_write(u64 val)
>   	write_pmcr(val);
>   }
>   
> +static u64 armv8pmu_pmcr_n_read(void)
> +{
> +	return FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read());
> +}
> +
>   static int armv8pmu_has_overflowed(u64 pmovsr)
>   {
>   	return !!(pmovsr & ARMV8_PMU_CNT_MASK_ALL);
> @@ -1312,6 +1324,54 @@ struct armv8pmu_probe_info {
>   	bool present;
>   };
>   
> +/**
> + * armv8pmu_reservation_is_valid() - Determine if reservation is allowed
> + * @host_counters: Number of host counters to reserve
> + *
> + * Determine if the number of host counters in the argument is an
> + * allowed reservation, 0 to NR_COUNTERS inclusive.
> + *
> + * Return: True if reservation allowed, false otherwise
> + */
> +static bool armv8pmu_reservation_is_valid(int host_counters)
> +{
> +	return host_counters >= 0 &&
> +		host_counters <= armv8pmu_pmcr_n_read();
> +}
> +
> +/**
> + * armv8pmu_partition() - Partition the PMU
> + * @pmu: Pointer to pmu being partitioned
> + * @host_counters: Number of host counters to reserve
> + *
> + * Partition the given PMU by taking a number of host counters to
> + * reserve and, if it is a valid reservation, recording the
> + * corresponding HPMN value in the max_guest_counters field of the PMU and
> + * clearing the guest-reserved counters from the counter mask.
> + *
> + * Return: 0 on success, -ERROR otherwise
> + */
> +static int armv8pmu_partition(struct arm_pmu *pmu, int host_counters)
> +{
> +	u8 nr_counters;
> +	u8 hpmn;
> +
> +	if (!armv8pmu_reservation_is_valid(host_counters)) {
> +		pr_err("PMU partition reservation of %d host counters is not valid", host_counters);
> +		return -EINVAL;
> +	}
> +
> +	nr_counters = armv8pmu_pmcr_n_read();
> +	hpmn = nr_counters - host_counters;
> +
> +	pmu->max_guest_counters = hpmn;
> +	armv8pmu_is_partitioned = true;
> +
> +	pr_info("Partitioned PMU with %d host counters -> %u guest counters", host_counters, hpmn);
> +
> +	return 0;
> +}
> +
>   static void __armv8pmu_probe_pmu(void *info)
>   {
>   	struct armv8pmu_probe_info *probe = info;
> @@ -1326,17 +1386,26 @@ static void __armv8pmu_probe_pmu(void *info)
>   
>   	cpu_pmu->pmuver = pmuver;
>   	probe->present = true;
> +	cpu_pmu->max_guest_counters = -1;
>   
>   	/* Read the nb of CNTx counters supported from PMNC */
> -	bitmap_set(cpu_pmu->cntr_mask,
> -		   0, FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read()));
> +	bitmap_set(cpu_pmu->hw_cntr_mask, 0, armv8pmu_pmcr_n_read());
>   
>   	/* Add the CPU cycles counter */
> -	set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask);
> +	set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->hw_cntr_mask);
>   
>   	/* Add the CPU instructions counter */
>   	if (pmuv3_has_icntr())
> -		set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask);
> +		set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->hw_cntr_mask);
> +
> +	bitmap_copy(cpu_pmu->cntr_mask, cpu_pmu->hw_cntr_mask, ARMPMU_MAX_HWEVENTS);
> +
> +	if (reserved_host_counters >= 0) {
> +		if (has_host_pmu_partition_support())
> +			armv8pmu_partition(cpu_pmu, reserved_host_counters);
> +		else
> +			pr_err("PMU partition is not supported");
> +	}
>   
>   	pmceid[0] = pmceid_raw[0] = read_pmceid0();
>   	pmceid[1] = pmceid_raw[1] = read_pmceid1();
> diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
> index 24a471cf59d56..95f404cdcb2df 100644
> --- a/include/kvm/arm_pmu.h
> +++ b/include/kvm/arm_pmu.h
> @@ -47,7 +47,10 @@ struct arm_pmu_entry {
>   	struct arm_pmu *arm_pmu;
>   };
>   
> +extern bool armv8pmu_is_partitioned;
> +
>   bool kvm_supports_guest_pmuv3(void);
> +bool has_host_pmu_partition_support(void);
>   #define kvm_arm_pmu_irq_initialized(v)	((v)->arch.pmu.irq_num >= VGIC_NR_SGIS)
>   u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
>   void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
> @@ -117,6 +120,11 @@ static inline bool kvm_supports_guest_pmuv3(void)
>   	return false;
>   }
>   
> +static inline bool has_host_pmu_partition_support(void)
> +{
> +	return false;
> +}
> +
>   #define kvm_arm_pmu_irq_initialized(v)	(false)
>   static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
>   					    u64 select_idx)
> diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
> index 52b37f7bdbf9e..f7b000bb3eca8 100644
> --- a/include/linux/perf/arm_pmu.h
> +++ b/include/linux/perf/arm_pmu.h
> @@ -109,6 +109,7 @@ struct arm_pmu {
>   	 */
>   	int		(*map_pmuv3_event)(unsigned int eventsel);
>   	DECLARE_BITMAP(cntr_mask, ARMPMU_MAX_HWEVENTS);
> +	DECLARE_BITMAP(hw_cntr_mask, ARMPMU_MAX_HWEVENTS);

I think this needs a comment or a clearer name. Both cntr_mask and 
hw_cntr_mask are used in KVM and the PMU driver and it's not immediately 
obvious what the difference is.





^ permalink raw reply

* Re: [PATCH] clk: rockchip: allow COMPILE_TEST builds
From: Brian Masney @ 2026-05-11 14:50 UTC (permalink / raw)
  To: Rosen Penev
  Cc: linux-rockchip, Michael Turquette, Stephen Boyd, Heiko Stuebner,
	Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
	open list:COMMON CLK FRAMEWORK, open list,
	moderated list:ARM/Rockchip SoC support,
	open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
In-Reply-To: <20260509003602.956186-1-rosenp@gmail.com>

On Fri, May 08, 2026 at 05:36:02PM -0700, Rosen Penev wrote:
> COMMON_CLK_ROCKCHIP already gates the Rockchip clock objects inside the
> Rockchip clock Makefile.  Allow selecting it for COMPILE_TEST and use it
> for the parent Makefile descent instead of ARCH_ROCKCHIP.
> 
> The per-SoC Rockchip clock symbols already have COMPILE_TEST dependencies,
> so this exposes the existing build coverage to other architectures without
> selecting the Rockchip platform.
> 
> Tested with:
> make LLVM=1 ARCH=loongarch drivers/clk/rockchip/
> 
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Rosen Penev <rosenp@gmail.com>

Reviewed-by: Brian Masney <bmasney@redhat.com>



^ permalink raw reply

* Re: [PATCH v7 10/20] KVM: arm64: Context swap Partitioned PMU guest registers
From: James Clark @ 2026-05-11 14:49 UTC (permalink / raw)
  To: Colton Lewis
  Cc: Alexandru Elisei, Paolo Bonzini, Jonathan Corbet, Russell King,
	Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
	Mingwei Zhang, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Mark Rutland, Shuah Khan, Ganapatrao Kulkarni, linux-doc,
	linux-kernel, linux-arm-kernel, kvmarm, linux-perf-users,
	linux-kselftest, kvm
In-Reply-To: <20260504211813.1804997-11-coltonlewis@google.com>



On 04/05/2026 10:18 pm, Colton Lewis wrote:
> Save and restore newly untrapped registers that can be directly
> accessed by the guest when the PMU is partitioned.
> 
> * PMEVCNTRn_EL0
> * PMCCNTR_EL0
> * PMSELR_EL0
> * PMCR_EL0
> * PMCNTEN_EL0
> * PMINTEN_EL1
> 
> If we know we are not partitioned (that is, using the emulated vPMU),
> then return immediately. A later patch will make this lazy so the
> context swaps don't happen unless the guest has accessed the PMU.
> 
> PMEVTYPER is handled in a following patch since we must apply the KVM
> event filter before writing values to hardware.
> 
> PMOVS guest counters are cleared to avoid the possibility of
> generating spurious interrupts when PMINTEN is written. This is fine
> because the virtual register for PMOVS is always the canonical value.
> 
> Signed-off-by: Colton Lewis <coltonlewis@google.com>
> ---
>   arch/arm/include/asm/arm_pmuv3.h |   4 +
>   arch/arm64/kvm/arm.c             |   2 +
>   arch/arm64/kvm/pmu-direct.c      | 169 +++++++++++++++++++++++++++++++
>   include/kvm/arm_pmu.h            |  16 +++
>   4 files changed, 191 insertions(+)
> 
> diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
> index 42d62aa48d0a6..eebc89bdab7a1 100644
> --- a/arch/arm/include/asm/arm_pmuv3.h
> +++ b/arch/arm/include/asm/arm_pmuv3.h
> @@ -235,6 +235,10 @@ static inline bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
>   {
>   	return false;
>   }
> +static inline u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
> +{
> +	return ~0;
> +}
>   
>   /* PMU Version in DFR Register */
>   #define ARMV8_PMU_DFR_VER_NI        0
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 410ffd41fd73a..a942f2bc13fc4 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -680,6 +680,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
>   		kvm_vcpu_load_vhe(vcpu);
>   	kvm_arch_vcpu_load_fp(vcpu);
>   	kvm_vcpu_pmu_restore_guest(vcpu);
> +	kvm_pmu_load(vcpu);
>   	if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
>   		kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
>   
> @@ -721,6 +722,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
>   	kvm_timer_vcpu_put(vcpu);
>   	kvm_vgic_put(vcpu);
>   	kvm_vcpu_pmu_restore_host(vcpu);
> +	kvm_pmu_put(vcpu);
>   	if (vcpu_has_nv(vcpu))
>   		kvm_vcpu_put_hw_mmu(vcpu);
>   	kvm_arm_vmid_clear_active();
> diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
> index 63ac72910e4b5..360d022d918d5 100644
> --- a/arch/arm64/kvm/pmu-direct.c
> +++ b/arch/arm64/kvm/pmu-direct.c
> @@ -9,6 +9,7 @@
>   #include <linux/perf/arm_pmuv3.h>
>   
>   #include <asm/arm_pmuv3.h>
> +#include <asm/kvm_emulate.h>
>   
>   /**
>    * has_host_pmu_partition_support() - Determine if partitioning is possible
> @@ -98,3 +99,171 @@ u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu)
>   
>   	return *host_data_ptr(nr_event_counters);
>   }
> +
> +/**
> + * kvm_pmu_host_counter_mask() - Compute bitmask of host-reserved counters
> + * @pmu: Pointer to arm_pmu struct
> + *
> + * Compute the bitmask that selects the host-reserved counters in the
> + * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters
> + * in HPMN..N
> + *
> + * Return: Bitmask
> + */
> +u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
> +{
> +	u8 nr_counters = *host_data_ptr(nr_event_counters);
> +
> +	if (kvm_pmu_is_partitioned(pmu))
> +		return GENMASK(nr_counters - 1, pmu->max_guest_counters);
> +
> +	return ARMV8_PMU_CNT_MASK_ALL;
> +}
> +
> +/**
> + * kvm_pmu_guest_counter_mask() - Compute bitmask of guest-reserved counters
> + * @pmu: Pointer to arm_pmu struct
> + *
> + * Compute the bitmask that selects the guest-reserved counters in the
> + * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters
> + * in 0..HPMN and the cycle and instruction counters.
> + *
> + * Return: Bitmask
> + */
> +u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu)
> +{
> +	if (kvm_pmu_is_partitioned(pmu))
> +		return ARMV8_PMU_CNT_MASK_C | GENMASK(pmu->max_guest_counters - 1, 0);
> +
> +	return 0;
> +}

Minor nit: slightly inconsistent use of types. Returns a u64 but doesn't 
use GENMASK_ULL and is also usually saved into a long when it's called.



^ permalink raw reply

* Re: [PATCH v7 13/20] KVM: arm64: Apply dynamic guest counter reservations
From: James Clark @ 2026-05-11 14:47 UTC (permalink / raw)
  To: Colton Lewis
  Cc: Alexandru Elisei, Paolo Bonzini, Jonathan Corbet, Russell King,
	Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
	Mingwei Zhang, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Mark Rutland, Shuah Khan, Ganapatrao Kulkarni, linux-doc,
	linux-kernel, linux-arm-kernel, kvmarm, linux-perf-users,
	linux-kselftest, kvm
In-Reply-To: <20260504211813.1804997-14-coltonlewis@google.com>



On 04/05/2026 10:18 pm, Colton Lewis wrote:
> Apply dynamic guest counter reservations by checking if the requested
> guest mask collides with any events the host has scheduled and calling
> pmu_perf_resched_update() with a hook that updates the mask of
> available counters in between schedule out and schedule in.
> 
> Signed-off-by: Colton Lewis <coltonlewis@google.com>
> ---
>   arch/arm64/kvm/pmu-direct.c  | 69 ++++++++++++++++++++++++++++++++++++
>   include/linux/perf/arm_pmu.h |  1 +
>   2 files changed, 70 insertions(+)
> 
> diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
> index 2252d3b905db9..14cc419dbafad 100644
> --- a/arch/arm64/kvm/pmu-direct.c
> +++ b/arch/arm64/kvm/pmu-direct.c
> @@ -100,6 +100,73 @@ u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu)
>   	return *host_data_ptr(nr_event_counters);
>   }
>   
> +/* Callback to update counter mask between perf scheduling */
> +static void kvm_pmu_update_mask(struct pmu *pmu, void *data)
> +{
> +	struct arm_pmu *arm_pmu = to_arm_pmu(pmu);
> +	unsigned long *new_mask = data;
> +
> +	bitmap_copy(arm_pmu->cntr_mask, new_mask, ARMPMU_MAX_HWEVENTS);
> +}
> +
> +/**
> + * kvm_pmu_set_guest_counters() - Handle dynamic counter reservations
> + * @cpu_pmu: struct arm_pmu to potentially modify
> + * @guest_mask: new guest mask for the pmu
> + *
> + * Check if guest counters will interfere with current host events and
> + * call into perf_pmu_resched_update if a reschedule is required.
> + */
> +static void kvm_pmu_set_guest_counters(struct arm_pmu *cpu_pmu, u64 guest_mask)
> +{
> +	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
> +	DECLARE_BITMAP(guest_bitmap, ARMPMU_MAX_HWEVENTS);
> +	DECLARE_BITMAP(new_mask, ARMPMU_MAX_HWEVENTS);
> +	bool need_resched = false;
> +
> +	bitmap_from_arr64(guest_bitmap, &guest_mask, ARMPMU_MAX_HWEVENTS);
> +	bitmap_copy(new_mask, cpu_pmu->hw_cntr_mask, ARMPMU_MAX_HWEVENTS);
> +
> +	if (guest_mask) {
> +		/* Subtract guest counters from available host mask */
> +		bitmap_andnot(new_mask, new_mask, guest_bitmap, ARMPMU_MAX_HWEVENTS);
> +
> +		/* Did we collide with an active host event? */
> +		if (bitmap_intersects(cpuc->used_mask, guest_bitmap, ARMPMU_MAX_HWEVENTS)) {
> +			int idx;
> +
> +			need_resched = true;
> +			cpuc->host_squeezed = true;
> +
> +			/* Look for pinned events that are about to be preempted */
> +			for_each_set_bit(idx, guest_bitmap, ARMPMU_MAX_HWEVENTS) {
> +				if (test_bit(idx, cpuc->used_mask) && cpuc->events[idx] &&
> +				    cpuc->events[idx]->attr.pinned) {
> +					pr_warn_ratelimited("perf: Pinned host event squeezed out by KVM guest PMU partition\n");

Hi Colton,

I get "perf: Pinned host event squeezed out by KVM guest PMU partition" 
even with arm_pmuv3.reserved_host_counters=3 for example. I would have 
expected any non zero value to stop the warning.

I think armv8pmu_get_single_idx() needs to be changed to allocate from 
the high end host counters first. A more complicated option would be 
checking to see if there are any non-pinned counters in the host 
reserved half when a new pinned counter is opened, then swapping the 
places of the new pinned and existing non-pinned counters so pinned 
always prefer being put into the host half. But it's probably not worth 
doing that.

James

> +					break;
> +				}
> +			}
> +		}
> +	} else {
> +		/*
> +		 * Restoring to hw_cntr_mask.
> +		 * Only resched if we previously squeezed an event.
> +		 */
> +		if (cpuc->host_squeezed) {
> +			need_resched = true;
> +			cpuc->host_squeezed = false;
> +		}
> +	}
> +
> +	if (need_resched) {
> +		/* Collision: run full perf reschedule */
> +		perf_pmu_resched_update(&cpu_pmu->pmu, kvm_pmu_update_mask, new_mask);
> +	} else {
> +		/* Host was never using guest counters anyway */
> +		bitmap_copy(cpu_pmu->cntr_mask, new_mask, ARMPMU_MAX_HWEVENTS);
> +	}
> +}
> +
>   /**
>    * kvm_pmu_host_counter_mask() - Compute bitmask of host-reserved counters
>    * @pmu: Pointer to arm_pmu struct
> @@ -218,6 +285,7 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu)
>   
>   	pmu = vcpu->kvm->arch.arm_pmu;
>   	guest_counters = kvm_pmu_guest_counter_mask(pmu);
> +	kvm_pmu_set_guest_counters(pmu, guest_counters);
>   	kvm_pmu_apply_event_filter(vcpu);
>   
>   	for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) {
> @@ -319,5 +387,6 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu)
>   	val = read_sysreg(pmintenset_el1);
>   	__vcpu_assign_sys_reg(vcpu, PMINTENSET_EL1, val & mask);
>   
> +	kvm_pmu_set_guest_counters(pmu, 0);
>   	preempt_enable();
>   }
> diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
> index f7b000bb3eca8..63f88fec5e80f 100644
> --- a/include/linux/perf/arm_pmu.h
> +++ b/include/linux/perf/arm_pmu.h
> @@ -75,6 +75,7 @@ struct pmu_hw_events {
>   
>   	/* Active events requesting branch records */
>   	unsigned int		branch_users;
> +	bool host_squeezed;
>   };
>   
>   enum armpmu_attr_groups {



^ permalink raw reply

* Re: [PATCH v4 05/15] clk: scmi: Use new simplified per-clock rate properties
From: Brian Masney @ 2026-05-11 14:47 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-clk,
	linux-renesas-soc, sudeep.holla, philip.radford, james.quinlan,
	f.fainelli, vincent.guittot, etienne.carriere, peng.fan,
	michal.simek, geert+renesas, kuninori.morimoto.gx,
	marek.vasut+renesas, Michael Turquette, Stephen Boyd, Peng Fan
In-Reply-To: <20260508153300.2224715-6-cristian.marussi@arm.com>

On Fri, May 08, 2026 at 04:32:50PM +0100, Cristian Marussi wrote:
> Use the new min_rate and max_rate unified properties that provide the
> proper values without having to consider the clock type.
> 
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: linux-clk@vger.kernel.org
> Reviewed-by: Peng Fan <peng.fan@nxp.com>
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>

Reviewed-by: Brian Masney <bmasney@redhat.com>



^ permalink raw reply

* Re: [PATCH v3] coresight: fix missing error code when trace ID is invalid
From: Leo Yan @ 2026-05-11 14:45 UTC (permalink / raw)
  To: Jie Gan
  Cc: Richard Cheng, Suzuki K Poulose, Mike Leach, James Clark,
	Alexander Shishkin, Tingwei Zhang, coresight, linux-arm-kernel,
	linux-kernel
In-Reply-To: <146d34e9-caa3-4119-a3f3-79515b3f2c46@oss.qualcomm.com>

On Mon, May 11, 2026 at 05:27:10PM +0800, Jie Gan wrote:

[...]

> > > @@ -755,10 +755,16 @@ void coresight_path_assign_trace_id(struct coresight_path *path,
> > >   		 * Non 0 is either success or fail.
> > >   		 */
> > >   		if (trace_id != 0) {
> > > -			path->trace_id = trace_id;
> > > -			return;
> > > +			if (IS_VALID_CS_TRACE_ID(trace_id)) {
> > > +				path->trace_id = trace_id;
> > > +				return 0;
> > > +			}
> > > +
> > > +			return -EINVAL;

I'd advocate a bit early exit style, like:

  /* 0 means the device has no ID assignment, so keep searching */
  if (trace_id == 0)
      continue;

  if (!IS_VALID_CS_TRACE_ID(trace_id))
      return -EINVAL;

  path->trace_id = trace_id;
  return 0;

Early exit can reduce indentation depth, and it handles simple cases
first and then the complex logic. In some cases (maye not this case),
we may benefit a bit from compiler optimization [1].

[1] https://xania.org/202512/18-partial-inlining

[...]

> The return value has been ignored in perf mode. It will introduce noisy by
> adding __must_check. So I think its better without __must_check?

Wouldn't it need to update perf mode as well?

Regarding __must_check, I searched Documentation but didn't find
guidance on when it should be used. I don't want to use this annotation
randomly (some functions use it and some not), this will be hard for
everyone to follow up.

IMO, it's fine not to use __must_check here. I would leave this to
Suzuki and other maintainers if have different opinions.

Thanks,
Leo


^ permalink raw reply

* Re: [PATCH v4 03/15] clk: scmi: Use new determine_rate clock operation
From: Brian Masney @ 2026-05-11 14:44 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-clk,
	linux-renesas-soc, sudeep.holla, philip.radford, james.quinlan,
	f.fainelli, vincent.guittot, etienne.carriere, peng.fan,
	michal.simek, geert+renesas, kuninori.morimoto.gx,
	marek.vasut+renesas, Michael Turquette, Stephen Boyd
In-Reply-To: <20260508153300.2224715-4-cristian.marussi@arm.com>

On Fri, May 08, 2026 at 04:32:48PM +0100, Cristian Marussi wrote:
> Use the Clock protocol layer determine_rate logic to calculate the closest
> rate that can be supported by a specific clock.
> 
> No functional change.
> 
> Cc: Brian Masney <bmasney@redhat.com>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: linux-clk@vger.kernel.org
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>

Reviewed-by: Brian Masney <bmasney@redhat.com>



^ permalink raw reply

* Re: [PATCH v4 01/15] clk: scmi: Fix clock rate rounding
From: Brian Masney @ 2026-05-11 14:44 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-clk,
	linux-renesas-soc, sudeep.holla, philip.radford, james.quinlan,
	f.fainelli, vincent.guittot, etienne.carriere, peng.fan,
	michal.simek, geert+renesas, kuninori.morimoto.gx,
	marek.vasut+renesas, Michael Turquette, Stephen Boyd
In-Reply-To: <20260508153300.2224715-2-cristian.marussi@arm.com>

On Fri, May 08, 2026 at 04:32:46PM +0100, Cristian Marussi wrote:
> While the do_div() helper used for rounding expects its divisor argument
> to be a 32bits quantity, the currently provided divisor parameter is a
> 64bit value that, as a consequence, is silently truncated and a possible
> source of bugs.
> 
> Fix by using the proper div64_ul helper.
> 
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: linux-clk@vger.kernel.org
> Fixes: 7a8655e19bdb ("clk: scmi: Fix the rounding of clock rate")
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>

Reviewed-by: Brian Masney <bmasney@redhat.com>



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox