* Re: [PATCH v2 07/16] thermal: mediatek: add PMIC thermal support
From: Andy Shevchenko @ 2026-05-12 7:04 UTC (permalink / raw)
To: rva333
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Sen Chu, Sean Wang, Macpaul Lin,
Lee Jones, Srinivas Kandagatla, Rafael J. Wysocki, Daniel Lezcano,
Zhang Rui, Lukasz Luba, linux-iio, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, linux-pm, Ben Grisdale
In-Reply-To: <20260512-mt6323-v2-7-3efcba579e88@protonmail.com>
On Tue, May 12, 2026 at 8:21 AM Roman Vivchar via B4 Relay
<devnull+rva333.protonmail.com@kernel.org> wrote:
>
> Add a new driver to support thermal monitoring on MediaTek PMICs.
>
> The driver retrieves calibration data from EFUSE, calculates the
> temperature using a linear interpolation, and registers the device with
> the thermal framework.
>
> Initial support is added for the mt6323 PMIC.
...
+ array_size.h
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/err.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/kernel.h>
No way the driver(s) nowadays use this header. Please, drop it and add
the ones that are really in use (there are missing ones).
> +#include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
Is it used?
> +#include <linux/thermal.h>
Missing types.h
> +#include <linux/units.h>
...
> +#define MT6323_ADC_VOLTAGE_RANGE 1800
> +#define MT6323_ADC_RESOLUTION 32768
These two ring a bell with the first code patch. Are they the same?
Can they be deduplicated?
...
> + ret = iio_read_channel_processed(sensor->adc_channel, &raw);
> + if (ret < 0) {
Do we need that ' < 0' part? What is the meaning of positive returned
value (if any) and why do we ignore that? Same question to all similar
checks in the whole series.
> + dev_err(sensor->mt->dev, "failed to read iio channel: %d\n",
> + ret);
> + return ret;
> + }
...
> + /*
> + * Temperature coefficient. The o_slope is a trim value applied to
> + * the base calibration
Respect English grammar and punctuation. Here is the period missing.
> + */
...
> +static int mtk_pmic_thermal_extract_efuse_mt6323(struct mtk_pmic_thermal *mt,
> + u16 *buf)
> +{
> + u32 reg;
> + s32 vts, degc_cali, o_slope, o_slope_sign, id;
> + int ret;
Better to keep reversed xmas tree order.
> + return 0;
> +}
...
> +static int mtk_pmic_thermal_get_calib_data(struct device *dev,
> + struct mtk_pmic_thermal *mt)
> +{
> + void *buf __free(kfree) = NULL;
This is a discouraged way of defining variables with __free(). See below.
> + struct nvmem_cell *cell;
> + size_t len;
> + int ret;
> +
> + cell = nvmem_cell_get(dev, NULL);
> + if (IS_ERR(cell))
> + return PTR_ERR(cell);
> + buf = nvmem_cell_read(cell, &len);
Should be rather here
void *buf __free(kfree) = nvmem_cell_read(cell, &len);
> + nvmem_cell_put(cell);
> +
> + if (IS_ERR(buf)) {
> + ret = PTR_ERR(buf);
> + buf = NULL;
> + return ret;
> + }
> +
> + if (len < 2 * sizeof(u16)) {
> + dev_err(dev, "invalid calibration data length\n");
> + return -EINVAL;
return dev_err_probe(...);
> + }
> +
> + ret = mt->data->extract_efuse(mt, buf);
> + if (ret) {
> + dev_info(dev, "device not calibrated, using default values\n");
> + mt->data->precalc(mt, MT6323_DEFAULT_VTS,
> + MT6323_DEFAULT_DEGC_CALI,
> + MT6323_DEFAULT_SLOPE,
> + MT6323_DEFAULT_SLOPE_SIGN);
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_pmic_thermal_init_sensor(struct mtk_pmic_thermal *mt, int id)
> +{
> + struct mtk_pmic_sensor *sensor = &mt->sensors[id];
> + struct device *dev = mt->dev;
> +
> + sensor->id = id;
> + sensor->mt = mt;
> +
> + if (mt->data->num_sensors > 1)
> + sensor->adc_channel = devm_iio_channel_get(dev, mt->data->sensors[id]);
> + else
> + sensor->adc_channel = devm_iio_channel_get(dev, NULL);
> +
Unneeded blank line as the above and below are coupled semantically.
> + if (IS_ERR(sensor->adc_channel))
> + return dev_err_probe(dev, PTR_ERR(sensor->adc_channel),
> + "failed to get channel %s\n",
> + mt->data->sensors[id]);
> +
> + sensor->tzdev = devm_thermal_of_zone_register(dev, id, sensor,
> + &mtk_pmic_thermal_ops);
> + if (IS_ERR(sensor->tzdev))
> + return dev_err_probe(dev, PTR_ERR(sensor->tzdev),
> + "failed to register thermal zone %d\n", id);
> +
> + return 0;
> +}
...
> +static const struct of_device_id mtk_pmic_thermal_of_match[] = {
> + { .compatible = "mediatek,mt6323-thermal",
> + .data = &mt6323_thermal_data },
> + { /* sentinel */ },
No comma for the terminator entry.
> +};
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v3 1/7] PCI: Add pci_host_common_link_train_delay() helper
From: Claudiu Beznea @ 2026-05-12 7:05 UTC (permalink / raw)
To: Hans Zhang, bhelgaas, lpieralisi, kwilczynski, mani, vigneshr,
jingoohan1, thomas.petazzoni, pali, ryder.lee, claudiu.beznea.uj,
mpillai
Cc: robh, s-vadapalli, linux-omap, linux-arm-kernel, linux-mediatek,
linux-renesas-soc, linux-pci, linux-kernel
In-Reply-To: <20260511055923.37117-2-18255117159@163.com>
Hi, Hans,
On 5/11/26 08:59, Hans Zhang wrote:
> PCIe r6.0, sec 6.6.1 (Conventional Reset) requires that for a Downstream
> Port supporting Link speeds greater than 5.0 GT/s, software must wait a
> minimum of 100 ms after Link training completes before sending any
> Configuration Request.
>
> Introduce a static inline helper pci_host_common_link_train_delay() that
> checks the given max_link_speed (2 = 5.0 GT/s, 3 = 8.0 GT/s, etc.) and
> calls msleep(100) only when the speed is greater than 5.0 GT/s.
>
> This allows multiple host controller drivers to share the same mandatory
> delay without duplicating the logic.
>
> Signed-off-by: Hans Zhang <18255117159@163.com>
> ---
> drivers/pci/controller/pci-host-common.h | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h
> index b5075d4bd7eb..d709f7e3e11a 100644
> --- a/drivers/pci/controller/pci-host-common.h
> +++ b/drivers/pci/controller/pci-host-common.h
> @@ -10,6 +10,9 @@
> #ifndef _PCI_HOST_COMMON_H
> #define _PCI_HOST_COMMON_H
>
> +#include <linux/delay.h>
> +#include "../pci.h"
> +
> struct pci_ecam_ops;
>
> int pci_host_common_probe(struct platform_device *pdev);
> @@ -20,4 +23,18 @@ void pci_host_common_remove(struct platform_device *pdev);
>
> struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
> struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops);
> +
> +/**
> + * pci_host_common_link_train_delay - Wait 100 ms if link speed > 5 GT/s
> + * @max_link_speed: the maximum link speed (2 = 5.0 GT/s, 3 = 8.0 GT/s, ...)
> + *
> + * Must be called after Link training completes and before the first
> + * Configuration Request is sent.
> + */
> +static inline void pci_host_common_link_train_delay(int max_link_speed)
> +{
> + if (max_link_speed > 2)
> + msleep(PCIE_RESET_CONFIG_WAIT_MS);
In case of RZ/G3S driver the max_link_speed is populated based on
"max-link-speed" DT property (by calling of_pci_get_max_link_speed()). My
understanding from [1] (and the review of the initial RZ/G3S driver support) is
that this is not a mandatory property (note also the "Host drivers *could* add
this" from [1]). At least for the RZ/G3S driver, in case the "max-link-speed" DT
property is not present in DT but the controller supports more than 5GT/s (that
is possible as the driver supports more controller variants), the max_link_speed
argument will be negative. In that case the msleep() will not be called. This
looks like an opposite of what the patch set is trying to achieve.
Also, if I'm not wrong, there is also the possibility of having the
max-link-speed > 2 but the downstream port to not support more than 5GT/s. In
that case the mspeep() would also be executed (but I think that wouldn't be
really an issue).
Thank you,
Claudiu
[1]
https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/pci/pci-bus-common.yaml#L117
Thank you,
Claudiu
^ permalink raw reply
* [PATCH 08/19] raid6: improve the public interface
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
Stop directly calling into function pointers from users of the RAID6 PQ
API, and provide exported functions with proper documentation and
API guarantees asserts where applicable instead.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
Documentation/crypto/async-tx-api.rst | 4 +-
crypto/async_tx/async_pq.c | 6 +-
crypto/async_tx/async_raid6_recov.c | 4 +-
drivers/md/raid5.c | 4 +-
fs/btrfs/raid56.c | 8 +-
include/linux/raid/pq.h | 28 ++--
lib/raid/raid6/algos.c | 139 +++++++++++++++++-
lib/raid/raid6/arm/recov_neon.c | 4 +-
.../raid6/loongarch/recov_loongarch_simd.c | 8 +-
lib/raid/raid6/recov.c | 4 +-
lib/raid/raid6/riscv/recov_rvv.c | 4 +-
lib/raid/raid6/s390/recov_s390xc.c | 4 +-
lib/raid/raid6/x86/recov_avx2.c | 4 +-
lib/raid/raid6/x86/recov_avx512.c | 4 +-
lib/raid/raid6/x86/recov_ssse3.c | 4 +-
15 files changed, 181 insertions(+), 48 deletions(-)
diff --git a/Documentation/crypto/async-tx-api.rst b/Documentation/crypto/async-tx-api.rst
index f88a7809385e..49fcfc66314a 100644
--- a/Documentation/crypto/async-tx-api.rst
+++ b/Documentation/crypto/async-tx-api.rst
@@ -82,9 +82,9 @@ xor_val xor a series of source buffers and set a flag if the
pq generate the p+q (raid6 syndrome) from a series of source buffers
pq_val validate that a p and or q buffer are in sync with a given series of
sources
-datap (raid6_datap_recov) recover a raid6 data block and the p block
+datap (raid6_recov_datap) recover a raid6 data block and the p block
from the given sources
-2data (raid6_2data_recov) recover 2 raid6 data blocks from the given
+2data (raid6_recov_2data) recover 2 raid6 data blocks from the given
sources
======== ====================================================================
diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c
index 0ce6f07b4e0d..f3574f80d1df 100644
--- a/crypto/async_tx/async_pq.c
+++ b/crypto/async_tx/async_pq.c
@@ -131,11 +131,11 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks,
}
}
if (submit->flags & ASYNC_TX_PQ_XOR_DST) {
- BUG_ON(!raid6_call.xor_syndrome);
+ BUG_ON(!raid6_can_xor_syndrome());
if (start >= 0)
- raid6_call.xor_syndrome(disks, start, stop, len, srcs);
+ raid6_xor_syndrome(disks, start, stop, len, srcs);
} else
- raid6_call.gen_syndrome(disks, len, srcs);
+ raid6_gen_syndrome(disks, len, srcs);
async_tx_sync_epilog(submit);
}
diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c
index f2dc6af6e6a7..305ea1421a3e 100644
--- a/crypto/async_tx/async_raid6_recov.c
+++ b/crypto/async_tx/async_raid6_recov.c
@@ -418,7 +418,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
else
ptrs[i] = page_address(blocks[i]) + offs[i];
- raid6_2data_recov(disks, bytes, faila, failb, ptrs);
+ raid6_recov_2data(disks, bytes, faila, failb, ptrs);
async_tx_sync_epilog(submit);
@@ -501,7 +501,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila,
else
ptrs[i] = page_address(blocks[i]) + offs[i];
- raid6_datap_recov(disks, bytes, faila, ptrs);
+ raid6_recov_datap(disks, bytes, faila, ptrs);
async_tx_sync_epilog(submit);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 0d76e82f4506..ebcb19317670 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6955,7 +6955,7 @@ raid5_store_rmw_level(struct mddev *mddev, const char *page, size_t len)
if (kstrtoul(page, 10, &new))
return -EINVAL;
- if (new != PARITY_DISABLE_RMW && !raid6_call.xor_syndrome)
+ if (new != PARITY_DISABLE_RMW && !raid6_can_xor_syndrome())
return -EINVAL;
if (new != PARITY_DISABLE_RMW &&
@@ -7646,7 +7646,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
conf->level = mddev->new_level;
if (conf->level == 6) {
conf->max_degraded = 2;
- if (raid6_call.xor_syndrome)
+ if (raid6_can_xor_syndrome())
conf->rmw_level = PARITY_ENABLE_RMW;
else
conf->rmw_level = PARITY_DISABLE_RMW;
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 08ee8f316d96..dabc9522e881 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1410,7 +1410,7 @@ static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int
rbio_qstripe_paddr(rbio, sector_nr, step_nr));
assert_rbio(rbio);
- raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ raid6_gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* raid5 */
memcpy(pointers[rbio->nr_data], pointers[0], step);
@@ -1987,10 +1987,10 @@ static void recover_vertical_step(struct btrfs_raid_bio *rbio,
}
if (failb == rbio->real_stripes - 2) {
- raid6_datap_recov(rbio->real_stripes, step,
+ raid6_recov_datap(rbio->real_stripes, step,
faila, pointers);
} else {
- raid6_2data_recov(rbio->real_stripes, step,
+ raid6_recov_2data(rbio->real_stripes, step,
faila, failb, pointers);
}
} else {
@@ -2644,7 +2644,7 @@ static bool verify_one_parity_step(struct btrfs_raid_bio *rbio,
if (has_qstripe) {
assert_rbio(rbio);
/* RAID6, call the library function to fill in our P/Q. */
- raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ raid6_gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* RAID5. */
memcpy(pointers[nr_data], pointers[0], step);
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index f27a866c287f..662c2669f63f 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -11,6 +11,25 @@
#include <linux/blkdev.h>
#include <linux/mm.h>
+/*
+ * While the RAID6 algorithm could in theory support 3 devices by just copying
+ * the data disk to the two parity disks, this configuration is not only useless
+ * because it is a suboptimal version of 3-way mirroring, but also easy to get
+ * wrong in architecture-optimized implementations due to special casing, so
+ * don't support it.
+ */
+#define RAID6_MIN_DISKS 4
+
+void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs);
+void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
+ void **ptrs);
+bool raid6_can_xor_syndrome(void);
+
+void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
+ void **ptrs);
+void raid6_recov_datap(int disks, size_t bytes, int faila,
+ void **ptrs);
+
/* Routine choices */
struct raid6_calls {
void (*gen_syndrome)(int, size_t, void **);
@@ -20,9 +39,6 @@ struct raid6_calls {
int priority; /* Relative priority ranking if non-zero */
};
-/* Selected algorithm */
-extern struct raid6_calls raid6_call;
-
/* Various routine sets */
extern const struct raid6_calls raid6_intx1;
extern const struct raid6_calls raid6_intx2;
@@ -92,10 +108,4 @@ extern const u8 raid6_gflog[256] __attribute__((aligned(256)));
extern const u8 raid6_gfinv[256] __attribute__((aligned(256)));
extern const u8 raid6_gfexi[256] __attribute__((aligned(256)));
-/* Recovery routines */
-extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
- void **ptrs);
-extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
- void **ptrs);
-
#endif /* LINUX_RAID_RAID6_H */
diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c
index 985c60bb00a4..683b97cb94ad 100644
--- a/lib/raid/raid6/algos.c
+++ b/lib/raid/raid6/algos.c
@@ -16,8 +16,85 @@
#include <linux/gfp.h>
#include <kunit/visibility.h>
-struct raid6_calls raid6_call;
-EXPORT_SYMBOL_GPL(raid6_call);
+static const struct raid6_recov_calls *raid6_recov_algo;
+
+/* Selected algorithm */
+static struct raid6_calls raid6_call;
+
+/**
+ * raid6_gen_syndrome - generate RAID6 P/Q parity
+ * @disks: number of "disks" to operate on including parity
+ * @bytes: length in bytes of each vector
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
+ * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
+ * @ptrs[@disks - 3].
+ *
+ * @disks must be at least 3, and the memory pointed to by each member of @ptrs
+ * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of
+ * 512.
+ *
+ * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
+ * algorithm.
+ */
+void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
+
+ raid6_call.gen_syndrome(disks, bytes, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
+
+/**
+ * raid6_xor_syndrome - update RAID6 P/Q parity
+ * @disks: number of "disks" to operate on including parity
+ * @start: first index into @disk to update
+ * @stop: last index into @disk to update
+ * @bytes: length in bytes of each vector
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
+ * @ptrs[@disks - 1] respectively for the memory pointed to by
+ * @ptrs[@start..@stop].
+ *
+ * This is used to update parity in place using the following sequence:
+ *
+ * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
+ * 2) update the the data in @ptrs[@start..@stop].
+ * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
+ *
+ * Data between @start and @stop that is not changed should be filled
+ * with a pointer to the kernel zero page.
+ *
+ * @disks must be at least 3, and the memory pointed to by each member of @ptrs
+ * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of
+ * 512. @stop must be larger or equal to @start.
+ */
+void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
+ void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
+ WARN_ON_ONCE(stop < start);
+
+ raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
+
+/*
+ * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
+ *
+ * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
+ */
+bool raid6_can_xor_syndrome(void)
+{
+ return !!raid6_call.xor_syndrome;
+}
+EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
const struct raid6_calls * const raid6_algos[] = {
#if defined(__i386__) && !defined(__arch_um__)
@@ -84,11 +161,58 @@ const struct raid6_calls * const raid6_algos[] = {
};
EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
-void (*raid6_2data_recov)(int, size_t, int, int, void **);
-EXPORT_SYMBOL_GPL(raid6_2data_recov);
+/**
+ * raid6_recov_2data - recover two missing data disks
+ * @disks: number of "disks" to operate on including parity
+ * @bytes: length in bytes of each vector
+ * @faila: first failed data disk index
+ * @failb: second failed data disk index
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
+ * data in the remaining disks and the two parities pointed to by the other
+ * indices between 0 and @disks - 1 in @ptrs. @disks includes the data disks
+ * and the two parities. @faila must be smaller than @failb.
+ *
+ * Memory pointed to by each pointer in @ptrs must be page aligned and is
+ * limited to %PAGE_SIZE.
+ */
+void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
+ void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(bytes > PAGE_SIZE);
+ WARN_ON_ONCE(failb <= faila);
+
+ raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_recov_2data);
+
+/**
+ * raid6_recov_datap - recover a missing data disk and missing P-parity
+ * @disks: number of "disks" to operate on including parity
+ * @bytes: length in bytes of each vector
+ * @faila: failed data disk index
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
+ * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
+ * pointed to by the other indices between 0 and @disks - 1 in @ptrs. @disks
+ * includes the data disks and the two parities.
+ *
+ * Memory pointed to by each pointer in @ptrs must be page aligned and is
+ * limited to %PAGE_SIZE.
+ */
+void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(bytes > PAGE_SIZE);
-void (*raid6_datap_recov)(int, size_t, int, void **);
-EXPORT_SYMBOL_GPL(raid6_datap_recov);
+ raid6_recov_algo->datap(disks, bytes, faila, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_recov_datap);
const struct raid6_recov_calls *const raid6_recov_algos[] = {
#ifdef CONFIG_X86
@@ -133,8 +257,7 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void)
best = *algo;
if (best) {
- raid6_2data_recov = best->data2;
- raid6_datap_recov = best->datap;
+ raid6_recov_algo = best;
pr_info("raid6: using %s recovery algorithm\n", best->name);
} else
diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c
index 9993bda5d3a6..4eb0efb44750 100644
--- a/lib/raid/raid6/arm/recov_neon.c
+++ b/lib/raid/raid6/arm/recov_neon.c
@@ -35,7 +35,7 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -69,7 +69,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
index 4d4563209647..7d4d349322b3 100644
--- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c
+++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
@@ -49,7 +49,7 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -201,7 +201,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
@@ -323,7 +323,7 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -440,7 +440,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c
index 211e1df28963..cc7e4dc1eaa6 100644
--- a/lib/raid/raid6/recov.c
+++ b/lib/raid/raid6/recov.c
@@ -37,7 +37,7 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -75,7 +75,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c
index f77d9c430687..3ff39826e33f 100644
--- a/lib/raid/raid6/riscv/recov_rvv.c
+++ b/lib/raid/raid6/riscv/recov_rvv.c
@@ -164,7 +164,7 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -199,7 +199,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c
index 0f32217b7123..2bc4c85174de 100644
--- a/lib/raid/raid6/s390/recov_s390xc.c
+++ b/lib/raid/raid6/s390/recov_s390xc.c
@@ -40,7 +40,7 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -84,7 +84,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c
index 325310c81e1c..bef82a38d8eb 100644
--- a/lib/raid/raid6/x86/recov_avx2.c
+++ b/lib/raid/raid6/x86/recov_avx2.c
@@ -34,7 +34,7 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -199,7 +199,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c
index 08de77fcb8bd..06c70e771eaa 100644
--- a/lib/raid/raid6/x86/recov_avx512.c
+++ b/lib/raid/raid6/x86/recov_avx512.c
@@ -43,7 +43,7 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -241,7 +241,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c
index 002bef1e0847..5ca7d56f23d8 100644
--- a/lib/raid/raid6/x86/recov_ssse3.c
+++ b/lib/raid/raid6/x86/recov_ssse3.c
@@ -36,7 +36,7 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -206,7 +206,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 4/8] ras: aest: Add panic_on_ue module parameter
From: Umang Chheda @ 2026-05-12 6:51 UTC (permalink / raw)
To: Ruidong Tian, Ruidong Tian, Tony Luck, Borislav Petkov,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, catalin.marinas, will, lpieralisi, rafael,
mark.rutland, Sudeep Holla
Cc: linux-arm-msm, linux-acpi, linux-arm-kernel, linux-edac,
linux-kernel, devicetree
In-Reply-To: <24e7a997-9479-447e-a1e2-cfab9a904668@linux.alibaba.com>
Hi Ruidong,
On 5/6/2026 1:36 PM, Ruidong Tian wrote:
>
>
> 在 2026/5/5 20:23, Umang Chheda 写道:
>> The driver unconditionally calls panic() whenever an unrecoverable,
>> uncontainable UE (UET_UC or UET_UEU) is detected. There is no way
>> for the user to suppress this behaviour, which makes it difficult to
>> test UE injection or to run in environments where a kernel panic on
>> every UE is undesirable.
>>
>> Add a module parameter `aest_panic_on_ue` When set to 0 the driver
>> logs the UE and continues instead of panicking.
>>
>> Usage:
>> # Boot time (kernel cmdline)
>> aest.aest_panic_on_ue=0
>>
>> # Runtime
>> echo 0 > /sys/module/aest/parameters/aest_panic_on_ue
>>
>> Signed-off-by: Umang Chheda <umang.chheda@oss.qualcomm.com>
>
> Hi Umang,
>
> Thanks for the patch.
>
> I understand that this parameter is intended to facilitate UE injection
> testing and to avoid kernel panics in certain environments. However, we
> need to carefully consider the potential risks.
>
> When a UC (Uncontainable Error) or UEU (Unrecoverable Error) occurs, the
> hardware state may be unpredictable, and data integrity cannot be
> guaranteed. Allowing the system to continue running instead of panicking
> in these scenarios could lead to silent data corruption or other
> unforeseen side effects, which poses a significant risk to system
> stability.
>
> For the sake of robustness and data safety, I do not believe we should
> expose an interface that allows users to suppress panic on such critical
> errors.
>
> If the goal is primarily to ease testing, I suggest handling this via
> local driver modifications in your test environment rather than
> upstreaming it as a configurable runtime option.
IMO, it would be useful to have a module parameter for this. In some
cases—outside of test scenarios—it’s necessary to avoid triggering a
kernel panic on UE errors.
Would it make sense to keep the default behavior as panic on UE, while
also providing a module parameter to disable it when needed? This way,
we can preserve the default safety behavior while avoiding the need for
local rebuilds just to change this setting.
Thanks,
Umang
>
> Best regards,
> Ruidong
>
>> ---
>> drivers/ras/aest/aest-core.c | 9 ++++++++-
>> 1 file changed, 8 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/ras/aest/aest-core.c b/drivers/ras/aest/aest-core.c
>> index b4f4c975da1d..9ce782a66edf 100644
>> --- a/drivers/ras/aest/aest-core.c
>> +++ b/drivers/ras/aest/aest-core.c
>> @@ -22,6 +22,11 @@ DEFINE_PER_CPU(struct aest_device, percpu_adev);
>> #undef pr_fmt
>> #define pr_fmt(fmt) "AEST: " fmt
>> +static bool aest_panic_on_ue;
>> +module_param(aest_panic_on_ue, bool, 0644);
>> +MODULE_PARM_DESC(aest_panic_on_ue,
>> + "Panic on unrecoverable error: 0=off 1=on (default: 1)");
>> +
>> #ifdef CONFIG_DEBUG_FS
>> struct dentry *aest_debugfs;
>> #endif
>> @@ -342,9 +347,11 @@ void aest_proc_record(struct aest_record *record,
>> void *data, bool fake)
>> aest_record_info(
>> record,
>> "Simulated error! Skip panic due to fault
>> injection\n");
>> - else
>> + else if (aest_panic_on_ue)
>> aest_panic(record, ®s,
>> "AEST: unrecoverable error encountered");
>> + else
>> + aest_record_err(record, "UE detected, panic suppressed\n");
>> }
>> aest_log(record, ®s);
>>
>
^ permalink raw reply
* Re: [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters
From: Shaopeng Tan (Fujitsu) @ 2026-05-12 6:50 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
jonathan.cameron@huawei.com, kobak@nvidia.com,
lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, peternewman@google.com,
punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
reinette.chatre@intel.com, rohit.mathew@arm.com,
scott@os.amperecomputing.com, sdonthineni@nvidia.com,
xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org
In-Reply-To: <20260511154147.557481-2-ben.horgan@arm.com>
Hello Ben,
> 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
https://lore.kernel.org/lkml/599617aa-aade-4fde-9efa-79d592f1ff3f@arm.com/
This concerns the comment I received last time.
I may not have fully understood it, so I'd like to clarify it once more.
Even if the system as a whole has multiple L3 caches and multiple NUMA nodes,
ABMC will be enabled as long as there is a single L3 cache and a single corresponding NUMA node.
Is my understanding correct?
If my understanding is correct, within the 'traffic_matches_l3()' function,
ABMC is enabled only when the entire system has a single NUMA node and a single L3 cache.
870 static bool traffic_matches_l3(struct mpam_class *class)
871 {
...
901
902 if (!cpumask_equal(tmp_cpumask, cpu_possible_mask)) {
903 pr_debug("There is more than one L3\n");
904 return false; *
905 }
...
912
913 if (num_possible_nodes() > 1) {
914 pr_debug("There is more than one numa node\n");
915 return false; *
916 }
917
...
926 }
Also, I'd also like to confirm one more thing.
The mpam_resctrl_pick_mba() function also calls traffic_matches_l3().
This suggests that, except in scenarios where the entire system has a single L3 cache and a single NUMA node (ABMC is disabled),
the Memory Bandwidth allocation will also be disabled.
Is this the intended behavior? If so, could you explain why?
Best regards,
Shaopeng TAN
^ permalink raw reply
* Re: [PATCH v2 06/16] nvmem: add mt6323 PMIC EFUSE driver
From: Andy Shevchenko @ 2026-05-12 6:47 UTC (permalink / raw)
To: rva333
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Sen Chu, Sean Wang, Macpaul Lin,
Lee Jones, Srinivas Kandagatla, Rafael J. Wysocki, Daniel Lezcano,
Zhang Rui, Lukasz Luba, linux-iio, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, linux-pm, Ben Grisdale
In-Reply-To: <20260512-mt6323-v2-6-3efcba579e88@protonmail.com>
On Tue, May 12, 2026 at 8:21 AM Roman Vivchar via B4 Relay
<devnull+rva333.protonmail.com@kernel.org> wrote:
>
> Add support for the EFUSE controller found in the Mediatek MT6323 PMIC.
> The MT6323 EFUSE stores 24 bytes of hardware-related data, such as
> thermal sensor calibration values.
...
> +#include <linux/err.h>
> +#include <linux/errno.h>
Doesn't seem to be used as err.h implies asm/errno.h that provides
basic error codes.
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/types.h>
...
> +static int mt6323_efuse_read(void *context, unsigned int offset, void *val,
> + size_t bytes)
> +{
> + struct regmap *map = context;
> + u32 tmp;
> + u16 *buf = val;
> + int ret;
> +
> + /*
> + * Manual regmap_read with loop is needed, because PWRAP is not
> + * a continuous MMIO space, but rather FSM which doesn't implement
> + * necessary read callback for the regmap_read_raw and regmap_read_bulk
> + * functions.
> + */
> + for (size_t i = 0; i < bytes; i += sizeof(*buf)) {
> + ret = regmap_read(map, MT6323_EFUSE_DOUT_BASE + offset + i, &tmp);
> + if (ret)
> + return ret;
> + *buf++ = (u16)tmp;
Why explicit casting?
> + }
> +
> + return 0;
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH] dt-bindings: arm-smmu: qcom: Add compatible for IPQ9650 SoC
From: Kathiravan Thirumoorthy @ 2026-05-12 6:47 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, iommu, devicetree, linux-kernel,
Kathiravan Thirumoorthy
Qualcomm IPQ9650 SoC include APPS SMMU that implements arm,mmu-500.
Document the compatible for the same.
Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
---
Documentation/devicetree/bindings/iommu/arm,smmu.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
index 06fb5c8e7547..226f16711d67 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
@@ -37,6 +37,7 @@ properties:
- enum:
- qcom,eliza-smmu-500
- qcom,glymur-smmu-500
+ - qcom,ipq9650-smmu-500
- qcom,kaanapali-smmu-500
- qcom,milos-smmu-500
- qcom,qcm2290-smmu-500
---
base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
change-id: 20260512-ipq9650_smmu_binding-36dc05d39860
Best regards,
--
Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
^ permalink raw reply related
* Re: [PATCH 0/8] ras: aest: extend AEST support to Device Tree frontend
From: Umang Chheda @ 2026-05-12 6:45 UTC (permalink / raw)
To: Ruidong Tian, Tony Luck, Borislav Petkov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
catalin.marinas, will, lpieralisi, rafael, mark.rutland,
Sudeep Holla
Cc: linux-arm-msm, linux-acpi, linux-arm-kernel, linux-edac,
linux-kernel, devicetree, Faruque Ansari
In-Reply-To: <8f1529af-f6fe-44bc-a4b1-c47a19824d1e@linux.alibaba.com>
Hi Ruidong,
On 5/6/2026 1:40 PM, Ruidong Tian wrote:
> Hi Umang,
>
> Thanks for your patch.
>
> Would it be okay if I include this patch in the next version of the AEST
> patch series? I will make sure to add your Signed-off-by line.
Yes, you can include this patch in the next version - we will represent
it and respond to all of the queries received on DT specific patches.
How should I be sharing the patch fixes based on comments received from
maintainers to you ? so that you can include in your patch series ?
Also, when is your plan to post the next version fixing the comments
received ?
Thanks,
Umang
>
> Best regards,
> Ruidong
>
> 在 2026/5/5 20:23, Umang Chheda 写道:
>> This series extends Tian Ruidong’s [1] ACPI-based AEST support series
>> to also cover Device Tree based platforms.
>>
>> While the existing AEST driver relies on the AEST ACPI table [3], many
>> embedded Arm platforms use Device Tree exclusively and cannot use the
>> driver today. This series adds a DT frontend that mirrors the ACPI
>> implementation and feeds the same core driver, keeping ACPI and DT
>> paths functionally equivalent.
>>
>> Along the way, several correctness issues were identified in the core
>> driver and are fixed in the first part of this series.
>>
>> The DT frontend is mutually exclusive with ACPI and does not introduce
>> any DT-specific logic into the core.
>>
>> How to test with QEMU
>> --------------------------
>> Tian Ruidong's QEMU fork [2] emulates AEST MMIO error records on the
>> virt machine. To test the DT frontend:
>>
>> 1. Build QEMU:
>>
>> git clone https://github.com/winterddd/qemu.git
>> cd qemu
>> git checkout c5e2d5dec9fd62ba622314c40bff0fbecb4dfb34
>> ./configure --target-list=aarch64-softmmu
>> make -j$(nproc)
>>
>> 2. Build the kernel with:
>>
>> CONFIG_OF_AEST=y
>> CONFIG_AEST=y
>> CONFIG_ARM64_RAS_EXTN=y
>> CONFIG_RAS=y
>>
>> 3. Add the following DT node to your virt machine DTB. The QEMU
>> fork maps DRAM error records at 0x090d0000 (SPI 44) and CMN
>> vendor records at 0x090e0000 (SPI 45):
>>
>> aest {
>> compatible = "arm,aest";
>> #address-cells = <2>;
>> #size-cells = <2>;
>> ranges;
>> interrupt-parent = <&gic>;
>>
>> /* DRAM memory node — MMIO at 0x090d0000, SPI 44 */
>> aest-dram0@90d0000 {
>> compatible = "arm,aest-memory";
>> arm,interface-type = <1>;
>> arm,group-format = <0>;
>> arm,interface-flags = <0x22>;
>> arm,num-records = <4>;
>> arm,record-impl = /bits/ 64 <0x0>;
>> arm,status-report = /bits/ 64 <0x0>;
>> arm,addr-mode = /bits/ 64 <0x0>;
>> arm,proximity-domain = <0>;
>> reg = <0x0 0x090d0000 0x0 0x1000>,
>> <0x0 0x090d0800 0x0 0x200>,
>> <0x0 0x090d0e00 0x0 0x100>;
>> reg-names = "errblock", "fault-inject",
>> "err-group";
>> interrupts = <GIC_SPI 44
>> IRQ_TYPE_LEVEL_HIGH>;
>> interrupt-names = "fhi";
>> };
>> };
>>
>> 4. Boot QEMU with acpi=off:
>>
>> ./qemu-system-aarch64 \
>> -machine virt,accel=tcg,gic-version=3 \
>> -cpu cortex-a57 -m 2G -smp 4 \
>> -kernel Image -dtb virt-aest.dtb \
>> -append "console=ttyAMA0 acpi=off earlycon" \
>> -nographic
>>
>> 5. Verify probe:
>>
>> dmesg | grep "DT AEST"
>> # Expected: DT AEST: registered 1 AEST error source(s) from DT
>> ls /sys/kernel/debug/aest/
>>
>> 6. Inject a CE error via the QEMU MMIO fault injection registers.
>> The QEMU device accepts 64-bit accesses only (use devmem with
>> the 64-bit width flag):
>>
>> devmem 0x090d0808 64 0x80000040 # CDOFF | CE inject
>>
>> This triggers QEMU's error_record_inj_write() which sets
>> ERR<n>STATUS.V=1 and asserts the IRQ. The kernel driver's
>> aest_irq_func() fires, reads the status, and logs:
>>
>> AEST: {1}[Hardware Error]: Hardware error from AEST memory.90d0000
>> AEST: {1}[Hardware Error]: Error from memory at SRAT proximity
>> domain 0x0
>>
>> Testing
>> -------
>> - Validated on Qualcomm's lemans-evk and monaco-evk board with DT boot.
>> - Validated CE and UE injection via debugfs soft_inject.
>> - Tested ACPI path is unaffected: ACPI boot continues to use
>> drivers/acpi/arm64/aest.c unchanged.
>>
>> [1] https://lore.kernel.org/lkml/20260122094656.73399-1-
>> tianruidong@linux.alibaba.com/
>> [2] https://github.com/winterddd/qemu/tree/error_record
>> [3] https://developer.arm.com/documentation/den0085/0200/
>>
>> Signed-off-by: Umang Chheda <umang.chheda@oss.qualcomm.com>
>> ---
>> Umang Chheda (8):
>> ras: aest: Fix shared processor node handling and error log
>> messages
>> ras: aest: Fix CE/UE error counts not incrementing in debugfs
>> ras: aest: Skip unimplemented records in debugfs
>> ras: aest: Add panic_on_ue module parameter
>> dt-bindings: arm: ras: Introduce bindings for ARM AEST
>> ras: aest: Add DT frontend for ARM AEST RAS error sources
>> arm64: dts: qcom: lemans: add AEST error nodes
>> arm64: dts: qcom: monaco: add AEST error nodes
>>
>> .../devicetree/bindings/arm/arm,aest.yaml | 406 +++++++++++++
>> arch/arm64/boot/dts/qcom/lemans.dtsi | 41 ++
>> arch/arm64/boot/dts/qcom/monaco.dtsi | 41 ++
>> drivers/ras/aest/Kconfig | 15 +-
>> drivers/ras/aest/Makefile | 2 +
>> drivers/ras/aest/aest-core.c | 63 +-
>> drivers/ras/aest/aest-of.c | 673 +++++++++++
>> ++++++++++
>> drivers/ras/aest/aest-sysfs.c | 27 +-
>> drivers/ras/aest/aest.h | 15 +-
>> include/dt-bindings/arm/aest.h | 43 ++
>> 10 files changed, 1310 insertions(+), 16 deletions(-)
>> ---
>> base-commit: a67b7fd0dd1f6ccf3d128dc2099cdb07af1f6a09
>> change-id: 20260505-aest-devicetree-support-a3722d90e1f5
>> prerequisite-message-id: <20260122094656.73399-1-
>> tianruidong@linux.alibaba.com>
>> prerequisite-patch-id: c5a7c6431c6c1e6351241e694ee053800039d41d
>> prerequisite-patch-id: 1f6e2c20829eee41a210dd8a538f1e8efcc65872
>> prerequisite-patch-id: 5556287e3f46c2ed2c0431c53c7782e87bcbd866
>> prerequisite-patch-id: 2edae0a136d7779b8f686181720e71d044a73311
>> prerequisite-patch-id: b5190b2844dcb01e72f87a59f3a29548795fdb82
>> prerequisite-patch-id: 7ba848583708b2ae776a7ce847bb056e3de7f77b
>> prerequisite-patch-id: 397e5b22802b67942435f4f2968f0b1e210ba0e8
>> prerequisite-patch-id: 2169f4b65537eecbd0ccbd2ad6b28c64ec44655d
>> prerequisite-patch-id: b626f85d98747595b3240bc49e6ad9c9dd5c0fa9
>> prerequisite-patch-id: 1323dfd2eebad2ef6514dbbce58ba08e8859f894
>> prerequisite-patch-id: 95b826e5e329408437a3ef336c4f45d4d74f82bb
>> prerequisite-patch-id: b60ff489a5a33c5d5220fa8144af7b7511769cba
>> prerequisite-patch-id: 43f35a52b8a3d13c938ff08083403c1d3bd0df8b
>> prerequisite-patch-id: c55d4e9117ca36d3c2cba82d550a618cb82bb745
>> prerequisite-patch-id: 3885e10f318ae8101d6909b35d92a976cc359e3c
>> prerequisite-patch-id: 92958cde05577f069c5659018a274bb39cfb6b24
>>
>> Best regards,
>> --
>> Umang Chheda <umang.chheda@oss.qualcomm.com>
>>
>
^ permalink raw reply
* Re: [PATCH v3 4/7] PCI: dwc: Use common pci_host_common_link_train_delay() helper
From: Manivannan Sadhasivam @ 2026-05-12 6:45 UTC (permalink / raw)
To: Krzysztof Wilczyński
Cc: Hans Zhang, bhelgaas, lpieralisi, vigneshr, jingoohan1,
thomas.petazzoni, pali, ryder.lee, claudiu.beznea.uj, mpillai,
robh, s-vadapalli, linux-omap, linux-arm-kernel, claudiu.beznea,
linux-mediatek, linux-renesas-soc, linux-pci, linux-kernel
In-Reply-To: <20260511070139.GA1096586@rocinante>
On Mon, May 11, 2026 at 04:02:42PM +0900, Krzysztof Wilczyński wrote:
> Hello,
>
> > - /*
> > - * As per PCIe r6.0, sec 6.6.1, a Downstream Port that supports Link
> > - * speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms
> > - * after Link training completes before sending a Configuration Request.
> > - */
> > - if (pci->max_link_speed > 2)
> > - msleep(PCIE_RESET_CONFIG_WAIT_MS);
> > + pci_host_common_link_train_delay(pci->max_link_speed);
>
> This comment could move to the helper you added.
>
Not required, as the macro definition in drivers/pci/pci.h already has an
elaborative comment with spec citation.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* [PATCH RESEND] clocksource: move NXP timer selection to drivers/clocksource
From: Enric Balletbo i Serra @ 2026-05-12 6:45 UTC (permalink / raw)
To: Russell King, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Daniel Lezcano, Thomas Gleixner
Cc: linux-arm-kernel, imx, linux-kernel, Enric Balletbo i Serra
From: Enric Balletbo i Serra <eballetb@redhat.com>
The Kconfig logic for selecting the scheduler clocksource on
NXP Vybrid (VF610) uses a `choice` block restricted to 32-bit ARM. This
prevents 64-bit architectures, such as the NXP S32 family, from enabling
the NXP Periodic Interrupt Timer (PIT) driver (CONFIG_NXP_PIT_TIMER).
Relocate the NXP clocksource selection from arch/arm/mach-imx/Kconfig to
drivers/clocksource/Kconfig. This allows the configuration to be shared
across different architectures.
Update the selection to include support for ARCH_S32 and add a "None"
option to allow using the standard ARM Architected Timer. The Vybrid
Global Timer option is now specifically restricted to 32-bit ARM SOC_VF610
platforms.
Fixes: bee33f22d7c3 ("clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support")
Signed-off-by: Enric Balletbo i Serra <eballetb@redhat.com>
---
arch/arm/mach-imx/Kconfig | 21 ---------------------
drivers/clocksource/Kconfig | 31 +++++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 21 deletions(-)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 6ea1bd55acf8..a361840d7a04 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -227,27 +227,6 @@ config SOC_VF610
help
This enables support for Freescale Vybrid VF610 processor.
-choice
- prompt "Clocksource for scheduler clock"
- depends on SOC_VF610
- default VF_USE_ARM_GLOBAL_TIMER
-
- config VF_USE_ARM_GLOBAL_TIMER
- bool "Use ARM Global Timer"
- depends on ARCH_MULTI_V7
- select ARM_GLOBAL_TIMER
- select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
- help
- Use the ARM Global Timer as clocksource
-
- config VF_USE_PIT_TIMER
- bool "Use PIT timer"
- select NXP_PIT_TIMER
- help
- Use SoC Periodic Interrupt Timer (PIT) as clocksource
-
-endchoice
-
endif
endif
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index fd9112706545..b5c88ec65802 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -794,4 +794,35 @@ config RTK_SYSTIMER
this option only when building for a Realtek platform or for compilation
testing.
+choice
+ prompt "NXP clocksource for scheduler clock"
+ depends on SOC_VF610 || ARCH_S32
+ # Default to Global Timer for Vybrid (32-bit)
+ default VF_USE_ARM_GLOBAL_TIMER if SOC_VF610
+ # Default to None for S32 (64-bit)
+ default VF_TIMER_NONE if ARCH_S32
+
+ config VF_USE_ARM_GLOBAL_TIMER
+ bool "Use NXP Vybrid Global Timer"
+ # This option is ONLY visible if we are on 32-bit ARM
+ depends on ARM && SOC_VF610
+ select ARM_GLOBAL_TIMER
+ select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+ help
+ Use the NXP Vybrid Global Timer as clocksource.
+
+ config VF_USE_PIT_TIMER
+ bool "Use NXP PIT timer"
+ select NXP_PIT_TIMER
+ help
+ Use NXP Periodic Interrupt Timer (PIT) as clocksource.
+
+ config VF_TIMER_NONE
+ bool "None (Use standard Arch Timer)"
+ help
+ Do not use any specific NXP timer driver. Use the standard
+ ARM Architected Timer instead.
+
+endchoice
+
endmenu
---
base-commit: 11439c4635edd669ae435eec308f4ab8a0804808
change-id: 20260302-fix-nxp-timer-9cb1fbd7afcd
Best regards,
--
Enric Balletbo i Serra <eballetb@redhat.com>
^ permalink raw reply related
* Re: [PATCH v2 05/16] iio: adc: mediatek: add mt6323 PMIC AUXADC driver
From: Andy Shevchenko @ 2026-05-12 6:43 UTC (permalink / raw)
To: rva333
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Sen Chu, Sean Wang, Macpaul Lin,
Lee Jones, Srinivas Kandagatla, Rafael J. Wysocki, Daniel Lezcano,
Zhang Rui, Lukasz Luba, linux-iio, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, linux-pm, Ben Grisdale
In-Reply-To: <20260512-mt6323-v2-5-3efcba579e88@protonmail.com>
On Tue, May 12, 2026 at 8:21 AM Roman Vivchar via B4 Relay
<devnull+rva333.protonmail.com@kernel.org> wrote:
>
> The mt6323 AUXADC is a 15-bit ADC used for system monitoring. This driver
> provides support for reading various channels including battery and
> charger voltages, battery and chip temperature, current sensing and
> accessory detection.
>
> Add a driver for the AUXADC found in the MediaTek mt6323 PMIC.
...
> +#include <linux/array_size.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/stringify.h>
+ time.h // USEC_PER_MSEC
> +#include <linux/types.h>
...
> +#define AUXADC_TRIM_CH2 (3 << 10)
> +#define AUXADC_TRIM_CH4 (3 << 8)
> +#define AUXADC_TRIM_CH5 (3 << 4)
> +#define AUXADC_TRIM_CH6 (3 << 2)
Without a comment it's hard to say if these are like masks or actual
values. Can you clarify that in the comment on top of these four?
> +#define VOLTAGE_FULL_RANGE 1800
Are there any units? Are they millivolts or is it just some scale?
...
> +#define MTK_PMIC_IIO_CHAN(_name, _idx, _ch_type) \
> +{ \
> + .type = _ch_type, \
> + .indexed = 1, \
> + .channel = _idx, \
> + .address = _idx, \
> + .datasheet_name = __stringify(_name), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE) \
Keep the trailing comma as this is not a terminator.
> +}
...
> +/**
> + * struct mt6323_auxadc - Main driver structure
> + * @regmap: Regmap from PWRAP
> + * @lock: Mutex to serialize AUXADC reading vs configuration
> + *
> + * The MediaTek MT6323 (as well as lot of other PMICs) have the following hierarchy:
> + * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM)
> + *
> + * Therefore, PWRAP regmap should be get using dev->parent->parent.
get --> obtained
> + */
...
> +static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc)
> +{
> + struct regmap *map = auxadc->regmap;
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(map, MT6323_AUXADC_CON19, &val);
> + if (ret)
> + return ret;
> +
> + /* The ADC is idle */
> + if (!(val & AUXADC_DECI_GDLY_MASK))
> + return 0;
> +
> + ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19, val,
> + !(val & AUXADC_ADC19_BUSY_MASK), 10, 500);
It's better to have a logical split
ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19,
val, !(val & AUXADC_ADC19_BUSY_MASK),
10, 500);
> + if (ret)
> + return ret;
> +
> + return regmap_clear_bits(map, MT6323_AUXADC_CON19,
> + AUXADC_DECI_GDLY_MASK);
> +}
...
> +static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc,
> + const struct iio_chan_spec *chan, int *out)
> +{
> + struct regmap *map = auxadc->regmap;
> + u32 val, reg = mt6323_auxadc_channel_to_reg(chan->address);
> + int ret;
> +
> + ret = regmap_read_poll_timeout(map, reg, val, (val & AUXADC_RDY_MASK),
Parentheses are not needed in this case. But I'm fine with it here as
it probably makes it easier to get the idea.
> + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
> + if (ret)
> + return ret;
> +
> + *out = FIELD_GET(AUXADC_DATA_MASK, val);
> +
> + return 0;
> +}
> +
> +static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan, int *val,
> + int *val2, long mask)
Logical split
static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
> +{
> + struct mt6323_auxadc *auxadc = iio_priv(indio_dev);
> + int ret, mult = 1;
Decouple assignment and definition. These types of assignments are
hard to maintain and might lead to subtle mistakes in the future.
> + if (mask == IIO_CHAN_INFO_RAW) {
> + guard(mutex)(&auxadc->lock);
> + ret = mt6323_auxadc_prepare_channel(auxadc);
> + if (ret)
> + return ret;
> +
> + ret = mt6323_auxadc_request(auxadc, chan->address);
> + if (ret)
> + return ret;
Please, add a comment with the reference to a datasheet (ideally)
explaining this sleep.
> + fsleep(300);
> +
> + ret = mt6323_auxadc_read(auxadc, chan, val);
> + if (ret)
> + return ret;
> + return IIO_VAL_INT;
> + } else if (mask == IIO_CHAN_INFO_SCALE) {
Redundant 'else'
> + if (chan->channel == MT6323_AUXADC_ISENSE ||
> + chan->channel == MT6323_AUXADC_BATSNS)
> + mult = 4;
> +
> + *val = mult * VOLTAGE_FULL_RANGE;
> + *val2 = AUXADC_PRECISE;
> +
> + return IIO_VAL_FRACTIONAL;
> + } else
Ditto, and it's the wrong style. Read the Coding Style documentation
to clarify this.
> + return -EINVAL;
> +}
...
> + ret = devm_mutex_init(dev, &auxadc->lock);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to initialize mutex\n");
Unneeded error message. Most likely it's -ENOMEM, which will be
ignored by dev_err_probe() anyway.
...
> + ret = devm_iio_device_register(dev, iio);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register iio device\n");
If you don't see the device, it's failed to register, do we need this message?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [LINUX PATCH] serial: xilinx_uartps: fix runtime PM race during probe
From: Shubhrajyoti Datta @ 2026-05-12 6:41 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Greg Kroah-Hartman, Jiri Slaby,
Michal Simek, Shubhrajyoti Datta, linux-serial, linux-arm-kernel
pm_runtime_enable() was called with usage_count=0, allowing the PM
core to immediately queue a deferred suspend via pm_runtime_work.
This raced with console write, causing cdns_runtime_suspend to
fire before the port was fully registered.
Hold a reference with pm_runtime_get_noresume() before enabling
runtime PM.
Fixes: d62100f1aac2 ("serial: xilinx_uartps: Add pm runtime support")
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
drivers/tty/serial/xilinx_uartps.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index a072b75dbaf2..e316cac4f35b 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1800,6 +1800,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
pm_runtime_set_active(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
pm_runtime_enable(&pdev->dev);
device_init_wakeup(port->dev, true);
@@ -1824,6 +1825,8 @@ static int cdns_uart_probe(struct platform_device *pdev)
"uart_add_one_port() failed; err=%i\n", rc);
goto err_out_pm_disable;
}
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/* This is not port which is used for console that's why clean it up */
@@ -1842,6 +1845,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
return 0;
err_out_pm_disable:
+ pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
--
2.49.1
^ permalink raw reply related
* Re: [PATCH v4] media: verisilicon: Create AV1 helper library
From: Hans Verkuil @ 2026-05-12 6:33 UTC (permalink / raw)
To: Benjamin Gaignard, nicolas.dufresne, p.zabel, mchehab, heiko
Cc: linux-kernel, linux-media, linux-rockchip, linux-arm-kernel,
kernel
In-Reply-To: <20260505081939.17230-1-benjamin.gaignard@collabora.com>
Hi Benjamin,
It looks much better now that it uses a struct.
I have some other comments, though:
On 05/05/2026 10:19, Benjamin Gaignard wrote:
> Regroup all none hardware related AV1 functions into a helper library.
> The goal is to avoid code duplication for future AV1 codecs.
>
> Tested on rock 5b board Fluster score remains the same 204/241.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> ---
> version 4:
> - change functions prototypes to use a structure.
> - rebased on v7.1-rc2 tag.
>
> drivers/media/platform/verisilicon/Makefile | 7 +-
> .../media/platform/verisilicon/hantro_av1.c | 782 ++++++++++++++
> .../media/platform/verisilicon/hantro_av1.h | 62 ++
> ...entropymode.c => hantro_av1_entropymode.c} | 18 +-
> ...entropymode.h => hantro_av1_entropymode.h} | 18 +-
> ...av1_filmgrain.c => hantro_av1_filmgrain.c} | 125 ++-
> .../verisilicon/hantro_av1_filmgrain.h | 48 +
> .../media/platform/verisilicon/hantro_hw.h | 7 +-
> .../verisilicon/rockchip_av1_filmgrain.h | 36 -
> .../verisilicon/rockchip_vpu981_hw_av1_dec.c | 977 ++----------------
> .../platform/verisilicon/rockchip_vpu_hw.c | 7 +-
> 11 files changed, 1076 insertions(+), 1011 deletions(-)
> create mode 100644 drivers/media/platform/verisilicon/hantro_av1.c
> create mode 100644 drivers/media/platform/verisilicon/hantro_av1.h
> rename drivers/media/platform/verisilicon/{rockchip_av1_entropymode.c => hantro_av1_entropymode.c} (99%)
> rename drivers/media/platform/verisilicon/{rockchip_av1_entropymode.h => hantro_av1_entropymode.h} (95%)
> rename drivers/media/platform/verisilicon/{rockchip_av1_filmgrain.c => hantro_av1_filmgrain.c} (85%)
> create mode 100644 drivers/media/platform/verisilicon/hantro_av1_filmgrain.h
> delete mode 100644 drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h
>
<snip>
> diff --git a/drivers/media/platform/verisilicon/hantro_av1.c b/drivers/media/platform/verisilicon/hantro_av1.c
> new file mode 100644
> index 000000000000..7258a749f7b5
> --- /dev/null
> +++ b/drivers/media/platform/verisilicon/hantro_av1.c
> @@ -0,0 +1,782 @@
<snip>
> +int hantro_av1_get_frame_index(struct hantro_ctx *ctx, int ref)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + u64 timestamp;
> + int i, idx = frame->ref_frame_idx[ref];
It's probably wise to check that 'ref' isn't out of bounds (< 0 or
>= V4L2_AV1_REFS_PER_FRAME).
> +
> + if (idx >= V4L2_AV1_TOTAL_REFS_PER_FRAME || idx < 0)
> + return AV1_INVALID_IDX;
> +
> + timestamp = frame->reference_frame_ts[idx];
> + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
> + if (!av1_dec->frame_refs[i].used)
> + continue;
> + if (av1_dec->frame_refs[i].timestamp == timestamp)
> + return i;
> + }
> +
> + return AV1_INVALID_IDX;
> +}
> +
> +int hantro_av1_get_order_hint(struct hantro_ctx *ctx, int ref)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + int idx = hantro_av1_get_frame_index(ctx, ref);
> +
> + if (idx != AV1_INVALID_IDX)
> + return av1_dec->frame_refs[idx].order_hint;
> +
> + return 0;
> +}
> +
> +int hantro_av1_frame_ref(struct hantro_ctx *ctx, u64 timestamp)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + int i;
> +
> + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) {
> + int j;
> +
> + if (av1_dec->frame_refs[i].used)
> + continue;
> +
> + av1_dec->frame_refs[i].width = frame->frame_width_minus_1 + 1;
> + av1_dec->frame_refs[i].height = frame->frame_height_minus_1 + 1;
> + av1_dec->frame_refs[i].mi_cols = DIV_ROUND_UP(frame->frame_width_minus_1 + 1, 8);
> + av1_dec->frame_refs[i].mi_rows = DIV_ROUND_UP(frame->frame_height_minus_1 + 1, 8);
> + av1_dec->frame_refs[i].timestamp = timestamp;
> + av1_dec->frame_refs[i].frame_type = frame->frame_type;
> + av1_dec->frame_refs[i].order_hint = frame->order_hint;
> + av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx);
> +
> + for (j = 0; j < V4L2_AV1_TOTAL_REFS_PER_FRAME; j++)
> + av1_dec->frame_refs[i].order_hints[j] = frame->order_hints[j];
> + av1_dec->frame_refs[i].used = true;
> + av1_dec->current_frame_index = i;
> +
> + return i;
> + }
> +
> + return AV1_INVALID_IDX;
> +}
> +
> +static void hantro_av1_frame_unref(struct hantro_ctx *ctx, int idx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> +
> + if (idx >= 0)
> + av1_dec->frame_refs[idx].used = false;
> +}
> +
> +void hantro_av1_clean_refs(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> +
> + int ref, idx;
> +
> + for (idx = 0; idx < AV1_MAX_FRAME_BUF_COUNT; idx++) {
> + u64 timestamp = av1_dec->frame_refs[idx].timestamp;
> + bool used = false;
> +
> + if (!av1_dec->frame_refs[idx].used)
> + continue;
> +
> + for (ref = 0; ref < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref++) {
> + if (ctrls->frame->reference_frame_ts[ref] == timestamp)
> + used = true;
> + }
> +
> + if (!used)
> + hantro_av1_frame_unref(ctx, idx);
> + }
> +}
> +
> +size_t hantro_av1_luma_size(struct hantro_ctx *ctx)
> +{
> + return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height;
> +}
> +
> +size_t hantro_av1_chroma_size(struct hantro_ctx *ctx)
> +{
> + size_t cr_offset = hantro_av1_luma_size(ctx);
> +
> + return ALIGN((cr_offset * 3) / 2, 64);
> +}
> +
> +static void hantro_av1_tiles_free(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> +
> + if (av1_dec->db_data_col.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->db_data_col.size,
> + av1_dec->db_data_col.cpu,
> + av1_dec->db_data_col.dma);
> + av1_dec->db_data_col.cpu = NULL;
> +
> + if (av1_dec->db_ctrl_col.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->db_ctrl_col.size,
> + av1_dec->db_ctrl_col.cpu,
> + av1_dec->db_ctrl_col.dma);
> + av1_dec->db_ctrl_col.cpu = NULL;
> +
> + if (av1_dec->cdef_col.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->cdef_col.size,
> + av1_dec->cdef_col.cpu, av1_dec->cdef_col.dma);
> + av1_dec->cdef_col.cpu = NULL;
> +
> + if (av1_dec->sr_col.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->sr_col.size,
> + av1_dec->sr_col.cpu, av1_dec->sr_col.dma);
> + av1_dec->sr_col.cpu = NULL;
> +
> + if (av1_dec->lr_col.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->lr_col.size,
> + av1_dec->lr_col.cpu, av1_dec->lr_col.dma);
> + av1_dec->lr_col.cpu = NULL;
> +}
> +
> +static int hantro_av1_tiles_reallocate(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
> + unsigned int num_tile_cols = tile_info->tile_cols;
> + unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64);
> + unsigned int height_in_sb = height / 64;
> + unsigned int stripe_num = ((height + 8) + 63) / 64;
> + size_t size;
> +
> + if (av1_dec->db_data_col.size >=
> + ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols)
> + return 0;
> +
> + hantro_av1_tiles_free(ctx);
> +
> + size = ALIGN(height * 12 * ctx->bit_depth / 8, 128) * num_tile_cols;
> + av1_dec->db_data_col.cpu = dma_alloc_coherent(vpu->dev, size,
> + &av1_dec->db_data_col.dma,
> + GFP_KERNEL);
> + if (!av1_dec->db_data_col.cpu)
> + goto buffer_allocation_error;
> + av1_dec->db_data_col.size = size;
> +
> + size = ALIGN(height * 2 * 16 / 4, 128) * num_tile_cols;
> + av1_dec->db_ctrl_col.cpu = dma_alloc_coherent(vpu->dev, size,
> + &av1_dec->db_ctrl_col.dma,
> + GFP_KERNEL);
> + if (!av1_dec->db_ctrl_col.cpu)
> + goto buffer_allocation_error;
> + av1_dec->db_ctrl_col.size = size;
> +
> + size = ALIGN(height_in_sb * 44 * ctx->bit_depth * 16 / 8, 128) * num_tile_cols;
> + av1_dec->cdef_col.cpu = dma_alloc_coherent(vpu->dev, size,
> + &av1_dec->cdef_col.dma,
> + GFP_KERNEL);
> + if (!av1_dec->cdef_col.cpu)
> + goto buffer_allocation_error;
> + av1_dec->cdef_col.size = size;
> +
> + size = ALIGN(height_in_sb * (3040 + 1280), 128) * num_tile_cols;
> + av1_dec->sr_col.cpu = dma_alloc_coherent(vpu->dev, size,
> + &av1_dec->sr_col.dma,
> + GFP_KERNEL);
> + if (!av1_dec->sr_col.cpu)
> + goto buffer_allocation_error;
> + av1_dec->sr_col.size = size;
> +
> + size = ALIGN(stripe_num * 1536 * ctx->bit_depth / 8, 128) * num_tile_cols;
> + av1_dec->lr_col.cpu = dma_alloc_coherent(vpu->dev, size,
> + &av1_dec->lr_col.dma,
> + GFP_KERNEL);
> + if (!av1_dec->lr_col.cpu)
> + goto buffer_allocation_error;
> + av1_dec->lr_col.size = size;
> +
> + av1_dec->num_tile_cols_allocated = num_tile_cols;
> + return 0;
> +
> +buffer_allocation_error:
> + hantro_av1_tiles_free(ctx);
> + return -ENOMEM;
> +}
> +
> +void hantro_av1_exit(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> +
> + if (av1_dec->global_model.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->global_model.size,
> + av1_dec->global_model.cpu,
> + av1_dec->global_model.dma);
> + av1_dec->global_model.cpu = NULL;
> +
> + if (av1_dec->tile_info.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->tile_info.size,
> + av1_dec->tile_info.cpu,
> + av1_dec->tile_info.dma);
> + av1_dec->tile_info.cpu = NULL;
> +
> + if (av1_dec->film_grain.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->film_grain.size,
> + av1_dec->film_grain.cpu,
> + av1_dec->film_grain.dma);
> + av1_dec->film_grain.cpu = NULL;
> +
> + if (av1_dec->prob_tbl.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->prob_tbl.size,
> + av1_dec->prob_tbl.cpu, av1_dec->prob_tbl.dma);
> + av1_dec->prob_tbl.cpu = NULL;
> +
> + if (av1_dec->prob_tbl_out.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->prob_tbl_out.size,
> + av1_dec->prob_tbl_out.cpu,
> + av1_dec->prob_tbl_out.dma);
> + av1_dec->prob_tbl_out.cpu = NULL;
> +
> + if (av1_dec->tile_buf.cpu)
> + dma_free_coherent(vpu->dev, av1_dec->tile_buf.size,
> + av1_dec->tile_buf.cpu, av1_dec->tile_buf.dma);
> + av1_dec->tile_buf.cpu = NULL;
> +
> + hantro_av1_tiles_free(ctx);
> +}
> +
> +int hantro_av1_init(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> +
> + memset(av1_dec, 0, sizeof(*av1_dec));
> +
> + av1_dec->global_model.cpu = dma_alloc_coherent(vpu->dev, GLOBAL_MODEL_SIZE,
> + &av1_dec->global_model.dma,
> + GFP_KERNEL);
> + if (!av1_dec->global_model.cpu)
> + return -ENOMEM;
> + av1_dec->global_model.size = GLOBAL_MODEL_SIZE;
> +
> + av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE,
> + &av1_dec->tile_info.dma,
> + GFP_KERNEL);
> + if (!av1_dec->tile_info.cpu)
> + return -ENOMEM;
> + av1_dec->tile_info.size = AV1_TILE_INFO_SIZE;
> +
> + av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev,
> + ALIGN(sizeof(struct hantro_av1_film_grain),
> + 2048),
> + &av1_dec->film_grain.dma,
> + GFP_KERNEL);
> + if (!av1_dec->film_grain.cpu)
> + return -ENOMEM;
> + av1_dec->film_grain.size = ALIGN(sizeof(struct hantro_av1_film_grain), 2048);
> +
> + av1_dec->prob_tbl.cpu = dma_alloc_coherent(vpu->dev,
> + ALIGN(sizeof(struct av1cdfs), 2048),
> + &av1_dec->prob_tbl.dma,
> + GFP_KERNEL);
> + if (!av1_dec->prob_tbl.cpu)
> + return -ENOMEM;
> + av1_dec->prob_tbl.size = ALIGN(sizeof(struct av1cdfs), 2048);
> +
> + av1_dec->prob_tbl_out.cpu = dma_alloc_coherent(vpu->dev,
> + ALIGN(sizeof(struct av1cdfs), 2048),
> + &av1_dec->prob_tbl_out.dma,
> + GFP_KERNEL);
> + if (!av1_dec->prob_tbl_out.cpu)
> + return -ENOMEM;
> + av1_dec->prob_tbl_out.size = ALIGN(sizeof(struct av1cdfs), 2048);
> + av1_dec->cdfs = &av1_dec->default_cdfs;
> + av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
> +
> + hantro_av1_set_default_cdfs(av1_dec->cdfs, av1_dec->cdfs_ndvc);
> +
> + av1_dec->tile_buf.cpu = dma_alloc_coherent(vpu->dev,
> + AV1_TILE_SIZE,
> + &av1_dec->tile_buf.dma,
> + GFP_KERNEL);
> + if (!av1_dec->tile_buf.cpu)
> + return -ENOMEM;
> + av1_dec->tile_buf.size = AV1_TILE_SIZE;
> +
> + return 0;
> +}
> +
> +int hantro_av1_prepare_run(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> +
> + ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
> + if (WARN_ON(!ctrls->sequence))
> + return -EINVAL;
> +
> + ctrls->tile_group_entry =
> + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
> + if (WARN_ON(!ctrls->tile_group_entry))
> + return -EINVAL;
> +
> + ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
> + if (WARN_ON(!ctrls->frame))
> + return -EINVAL;
> +
> + ctrls->film_grain =
> + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
> +
> + return hantro_av1_tiles_reallocate(ctx);
> +}
> +
> +static int hantro_av1_get_msb(u32 n)
> +{
> + if (n == 0)
> + return 0;
> + return 31 ^ __builtin_clz(n);
> +}
> +
> +static short hantro_av1_resolve_divisor_32(u32 d, short *shift)
> +{
> + int f;
> + u64 e;
> +
> + *shift = hantro_av1_get_msb(d);
> + /* e is obtained from D after resetting the most significant 1 bit. */
> + e = d - ((u32)1 << *shift);
> + /* Get the most significant DIV_LUT_BITS (8) bits of e into f */
> + if (*shift > DIV_LUT_BITS)
> + f = AV1_DIV_ROUND_UP_POW2(e, *shift - DIV_LUT_BITS);
> + else
> + f = e << (DIV_LUT_BITS - *shift);
> + if (f > DIV_LUT_NUM)
> + return -1;
> + *shift += DIV_LUT_PREC_BITS;
> + /* Use f as lookup into the precomputed table of multipliers */
> + return div_lut[f];
> +}
> +
> +static void hantro_av1_get_shear_params(const u32 *params, s64 *alpha,
> + s64 *beta, s64 *gamma, s64 *delta)
> +{
> + const int *mat = params;
> + short shift;
> + short y;
> + long long gv, dv;
> +
> + if (mat[2] <= 0)
> + return;
> +
> + *alpha = clamp_val(mat[2] - (1 << WARPEDMODEL_PREC_BITS), S16_MIN, S16_MAX);
> + *beta = clamp_val(mat[3], S16_MIN, S16_MAX);
> +
> + y = hantro_av1_resolve_divisor_32(abs(mat[2]), &shift) * (mat[2] < 0 ? -1 : 1);
> +
> + gv = ((long long)mat[4] * (1 << WARPEDMODEL_PREC_BITS)) * y;
> +
> + *gamma = clamp_val((int)AV1_DIV_ROUND_UP_POW2_SIGNED(gv, shift), S16_MIN, S16_MAX);
> +
> + dv = ((long long)mat[3] * mat[4]) * y;
> + *delta = clamp_val(mat[5] -
> + (int)AV1_DIV_ROUND_UP_POW2_SIGNED(dv, shift) - (1 << WARPEDMODEL_PREC_BITS),
> + S16_MIN, S16_MAX);
> +
> + *alpha = AV1_DIV_ROUND_UP_POW2_SIGNED(*alpha, WARP_PARAM_REDUCE_BITS)
> + * (1 << WARP_PARAM_REDUCE_BITS);
> + *beta = AV1_DIV_ROUND_UP_POW2_SIGNED(*beta, WARP_PARAM_REDUCE_BITS)
> + * (1 << WARP_PARAM_REDUCE_BITS);
> + *gamma = AV1_DIV_ROUND_UP_POW2_SIGNED(*gamma, WARP_PARAM_REDUCE_BITS)
> + * (1 << WARP_PARAM_REDUCE_BITS);
> + *delta = AV1_DIV_ROUND_UP_POW2_SIGNED(*delta, WARP_PARAM_REDUCE_BITS)
> + * (1 << WARP_PARAM_REDUCE_BITS);
> +}
> +
> +void hantro_av1_set_global_model(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + const struct v4l2_av1_global_motion *gm = &frame->global_motion;
> + u8 *dst = av1_dec->global_model.cpu;
> + int ref_frame, i;
> +
> + memset(dst, 0, GLOBAL_MODEL_SIZE);
> + for (ref_frame = 0; ref_frame < V4L2_AV1_REFS_PER_FRAME; ++ref_frame) {
> + s64 alpha = 0, beta = 0, gamma = 0, delta = 0;
> +
> + for (i = 0; i < 6; ++i) {
> + if (i == 2)
> + *(s32 *)dst =
> + gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][3];
> + else if (i == 3)
> + *(s32 *)dst =
> + gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][2];
> + else
> + *(s32 *)dst =
> + gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][i];
> + dst += 4;
> + }
> +
> + if (gm->type[V4L2_AV1_REF_LAST_FRAME + ref_frame] <= V4L2_AV1_WARP_MODEL_AFFINE)
> + hantro_av1_get_shear_params(&gm->params[V4L2_AV1_REF_LAST_FRAME + ref_frame][0],
> + &alpha, &beta, &gamma, &delta);
> +
> + *(s16 *)dst = alpha;
> + dst += 2;
> + *(s16 *)dst = beta;
> + dst += 2;
> + *(s16 *)dst = gamma;
> + dst += 2;
> + *(s16 *)dst = delta;
> + dst += 2;
> + }
> +}
> +
> +int hantro_av1_tile_log2(int target)
> +{
> + int k;
> +
> + /*
> + * returns the smallest value for k such that 1 << k is greater
> + * than or equal to target
> + */
> + for (k = 0; (1 << k) < target; k++)
> + ;
> +
> + return k;
> +}
> +
> +int hantro_av1_get_dist(struct hantro_ctx *ctx, int a, int b)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + int bits = ctrls->sequence->order_hint_bits - 1;
> + int diff, m;
> +
> + if (!ctrls->sequence->order_hint_bits)
> + return 0;
> +
> + diff = a - b;
> + m = 1 << bits;
> + diff = (diff & (m - 1)) - (diff & m);
> +
> + return diff;
> +}
> +
> +void hantro_av1_set_frame_sign_bias(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + const struct v4l2_ctrl_av1_sequence *sequence = ctrls->sequence;
> + int i;
> +
> + if (!sequence->order_hint_bits || IS_INTRA(frame->frame_type)) {
> + for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++)
> + av1_dec->ref_frame_sign_bias[i] = 0;
> +
> + return;
> + }
> + // Identify the nearest forward and backward references.
> + for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; i++) {
> + if (hantro_av1_get_frame_index(ctx, i) >= 0) {
> + int rel_off =
> + hantro_av1_get_dist(ctx,
> + hantro_av1_get_order_hint(ctx, i),
> + frame->order_hint);
> + av1_dec->ref_frame_sign_bias[i + 1] = (rel_off <= 0) ? 0 : 1;
> + }
> + }
> +}
> +
> +void hantro_av1_init_scaling_function(const u8 *values, const u8 *scaling,
> + u8 num_points, u8 *scaling_lut)
> +{
> + int i, point;
> +
> + if (num_points == 0) {
> + memset(scaling_lut, 0, 256);
> + return;
> + }
> +
> + for (point = 0; point < num_points - 1; point++) {
> + int x;
> + s32 delta_y = scaling[point + 1] - scaling[point];
> + s32 delta_x = values[point + 1] - values[point];
> + s64 delta =
> + delta_x ? delta_y * ((65536 + (delta_x >> 1)) /
> + delta_x) : 0;
> +
> + for (x = 0; x < delta_x; x++) {
> + scaling_lut[values[point] + x] =
> + scaling[point] +
> + (s32)((x * delta + 32768) >> 16);
> + }
> + }
> +
> + for (i = values[num_points - 1]; i < 256; i++)
> + scaling_lut[i] = scaling[num_points - 1];
> +}
> +
> +void hantro_av1_set_tile_info(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
> + const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
> + ctrls->tile_group_entry;
> + u8 *dst = av1_dec->tile_info.cpu;
> + int tile0, tile1;
> +
> + memset(dst, 0, av1_dec->tile_info.size);
> +
> + for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
> + for (tile1 = 0; tile1 < tile_info->tile_rows; tile1++) {
> + int tile_id = tile1 * tile_info->tile_cols + tile0;
> + u32 start, end;
> + u32 y0 =
> + tile_info->height_in_sbs_minus_1[tile1] + 1;
> + u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
> +
> + /* tile size in SB units (width,height) */
> + *dst++ = x0;
> + *dst++ = 0;
> + *dst++ = 0;
> + *dst++ = 0;
> + *dst++ = y0;
> + *dst++ = 0;
> + *dst++ = 0;
> + *dst++ = 0;
> +
> + /* tile start position */
> + start = group_entry[tile_id].tile_offset - group_entry[0].tile_offset;
> + *dst++ = start & 255;
> + *dst++ = (start >> 8) & 255;
> + *dst++ = (start >> 16) & 255;
> + *dst++ = (start >> 24) & 255;
> +
> + /* number of bytes in tile data */
> + end = start + group_entry[tile_id].tile_size;
> + *dst++ = end & 255;
> + *dst++ = (end >> 8) & 255;
> + *dst++ = (end >> 16) & 255;
> + *dst++ = (end >> 24) & 255;
> + }
> + }
> +}
> +
> +bool hantro_av1_is_lossless(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + const struct v4l2_av1_segmentation *segmentation = &frame->segmentation;
> + const struct v4l2_av1_quantization *quantization = &frame->quantization;
> + int i;
> +
> + for (i = 0; i < V4L2_AV1_MAX_SEGMENTS; i++) {
> + int qindex = quantization->base_q_idx;
> +
> + if (segmentation->feature_enabled[i] &
> + V4L2_AV1_SEGMENT_FEATURE_ENABLED(V4L2_AV1_SEG_LVL_ALT_Q)) {
> + qindex += segmentation->feature_data[i][V4L2_AV1_SEG_LVL_ALT_Q];
> + }
> + qindex = clamp(qindex, 0, 255);
> +
> + if (qindex ||
> + quantization->delta_q_y_dc ||
> + quantization->delta_q_u_dc ||
> + quantization->delta_q_u_ac ||
> + quantization->delta_q_v_dc ||
> + quantization->delta_q_v_ac)
> + return false;
> + }
> +
> + return true;
> +}
> +
> +void hantro_av1_update_prob(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + bool frame_is_intra = IS_INTRA(frame->frame_type);
> + struct av1cdfs *out_cdfs = (struct av1cdfs *)av1_dec->prob_tbl_out.cpu;
> + int i;
> +
> + if (frame->flags & V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF)
> + return;
> +
> + for (i = 0; i < NUM_REF_FRAMES; i++) {
> + if (frame->refresh_frame_flags & BIT(i)) {
> + struct mvcdfs stored_mv_cdf;
> +
> + hantro_av1_get_cdfs(ctx, i);
> + stored_mv_cdf = av1_dec->cdfs->mv_cdf;
> + *av1_dec->cdfs = *out_cdfs;
> + if (frame_is_intra) {
> + av1_dec->cdfs->mv_cdf = stored_mv_cdf;
> + *av1_dec->cdfs_ndvc = out_cdfs->mv_cdf;
> + }
> + hantro_av1_store_cdfs(ctx, frame->refresh_frame_flags);
> + break;
> + }
> + }
> +}
> +
> +void hantro_av1_set_prob(struct hantro_ctx *ctx)
> +{
> + struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
> + struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> + const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> + const struct v4l2_av1_quantization *quantization = &frame->quantization;
> + bool error_resilient_mode =
> + !!(frame->flags & V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE);
> + bool frame_is_intra = IS_INTRA(frame->frame_type);
> +
> + if (error_resilient_mode || frame_is_intra ||
> + frame->primary_ref_frame == AV1_PRIMARY_REF_NONE) {
> + av1_dec->cdfs = &av1_dec->default_cdfs;
> + av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
> + hantro_av1_default_coeff_probs(quantization->base_q_idx,
> + av1_dec->cdfs);
> + } else {
> + hantro_av1_get_cdfs(ctx, frame->ref_frame_idx[frame->primary_ref_frame]);
There is no sanity check for frame->primary_ref_frame to prevent out of bounds
array access. But perhaps this has to be checked elsewhere?
> + }
> + hantro_av1_store_cdfs(ctx, frame->refresh_frame_flags);
> +
> + memcpy(av1_dec->prob_tbl.cpu, av1_dec->cdfs, sizeof(struct av1cdfs));
> +
> + if (frame_is_intra) {
> + int mv_offset = offsetof(struct av1cdfs, mv_cdf);
> + /* Overwrite MV context area with intrabc MV context */
> + memcpy(av1_dec->prob_tbl.cpu + mv_offset, av1_dec->cdfs_ndvc,
> + sizeof(struct mvcdfs));
> + }
> +}
<snip>
> diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> index e4e21ad37323..5bf1f2689fbc 100644
> --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> @@ -7,622 +7,35 @@
<snip>
> static void rockchip_vpu981_av1_dec_set_cdef(struct hantro_ctx *ctx)
> @@ -1617,12 +846,12 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> int ref_ind = 0;
> int rf, idx;
>
> - alt_frame_offset = rockchip_vpu981_get_order_hint(ctx, ALT_BUF_IDX);
> - gld_frame_offset = rockchip_vpu981_get_order_hint(ctx, GLD_BUF_IDX);
> - bwd_frame_offset = rockchip_vpu981_get_order_hint(ctx, BWD_BUF_IDX);
> - alt2_frame_offset = rockchip_vpu981_get_order_hint(ctx, ALT2_BUF_IDX);
> + alt_frame_offset = hantro_av1_get_order_hint(ctx, ALT_BUF_IDX);
> + gld_frame_offset = hantro_av1_get_order_hint(ctx, GLD_BUF_IDX);
> + bwd_frame_offset = hantro_av1_get_order_hint(ctx, BWD_BUF_IDX);
> + alt2_frame_offset = hantro_av1_get_order_hint(ctx, ALT2_BUF_IDX);
>
> - idx = rockchip_vpu981_get_frame_index(ctx, LST_BUF_IDX);
> + idx = hantro_av1_get_frame_index(ctx, LST_BUF_IDX);
> if (idx >= 0) {
> int alt_frame_offset_in_lst =
> av1_dec->frame_refs[idx].order_hints[V4L2_AV1_REF_ALTREF_FRAME];
> @@ -1644,8 +873,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> ref_stamp--;
> }
>
> - idx = rockchip_vpu981_get_frame_index(ctx, BWD_BUF_IDX);
> - if (rockchip_vpu981_av1_dec_get_dist(ctx, bwd_frame_offset, cur_frame_offset) > 0) {
> + idx = hantro_av1_get_frame_index(ctx, BWD_BUF_IDX);
Can idx be AV1_INVALID_IDX? It's checked for LST_BUF_IDX, but not for BWD_BUF_IDX and others below.
> + if (hantro_av1_get_dist(ctx, bwd_frame_offset, cur_frame_offset) > 0) {
> int bwd_mi_cols = av1_dec->frame_refs[idx].mi_cols;
> int bwd_mi_rows = av1_dec->frame_refs[idx].mi_rows;
> bool bwd_intra_only =
> @@ -1659,8 +888,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> }
> }
>
> - idx = rockchip_vpu981_get_frame_index(ctx, ALT2_BUF_IDX);
> - if (rockchip_vpu981_av1_dec_get_dist(ctx, alt2_frame_offset, cur_frame_offset) > 0) {
> + idx = hantro_av1_get_frame_index(ctx, ALT2_BUF_IDX);
> + if (hantro_av1_get_dist(ctx, alt2_frame_offset, cur_frame_offset) > 0) {
> int alt2_mi_cols = av1_dec->frame_refs[idx].mi_cols;
> int alt2_mi_rows = av1_dec->frame_refs[idx].mi_rows;
> bool alt2_intra_only =
> @@ -1674,8 +903,8 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> }
> }
>
> - idx = rockchip_vpu981_get_frame_index(ctx, ALT_BUF_IDX);
> - if (rockchip_vpu981_av1_dec_get_dist(ctx, alt_frame_offset, cur_frame_offset) > 0 &&
> + idx = hantro_av1_get_frame_index(ctx, ALT_BUF_IDX);
> + if (hantro_av1_get_dist(ctx, alt_frame_offset, cur_frame_offset) > 0 &&
> ref_stamp >= 0) {
> int alt_mi_cols = av1_dec->frame_refs[idx].mi_cols;
> int alt_mi_rows = av1_dec->frame_refs[idx].mi_rows;
> @@ -1690,7 +919,7 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> }
> }
>
> - idx = rockchip_vpu981_get_frame_index(ctx, LST2_BUF_IDX);
> + idx = hantro_av1_get_frame_index(ctx, LST2_BUF_IDX);
> if (idx >= 0 && ref_stamp >= 0) {
> int lst2_mi_cols = av1_dec->frame_refs[idx].mi_cols;
> int lst2_mi_rows = av1_dec->frame_refs[idx].mi_rows;
> @@ -1706,14 +935,14 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> }
>
> for (rf = 0; rf < V4L2_AV1_TOTAL_REFS_PER_FRAME - 1; ++rf) {
> - idx = rockchip_vpu981_get_frame_index(ctx, rf);
> + idx = hantro_av1_get_frame_index(ctx, rf);
> if (idx >= 0) {
> - int rf_order_hint = rockchip_vpu981_get_order_hint(ctx, rf);
> + int rf_order_hint = hantro_av1_get_order_hint(ctx, rf);
>
> cur_offset[rf] =
> - rockchip_vpu981_av1_dec_get_dist(ctx, cur_frame_offset, rf_order_hint);
> + hantro_av1_get_dist(ctx, cur_frame_offset, rf_order_hint);
> cur_roffset[rf] =
> - rockchip_vpu981_av1_dec_get_dist(ctx, rf_order_hint, cur_frame_offset);
> + hantro_av1_get_dist(ctx, rf_order_hint, cur_frame_offset);
> } else {
> cur_offset[rf] = 0;
> cur_roffset[rf] = 0;
> @@ -1736,32 +965,32 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> if (use_ref_frame_mvs && ref_ind > 0 &&
> cur_offset[mf_types[0] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
> cur_offset[mf_types[0] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
> - int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[0]);
> - int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[0]);
> + int rf = hantro_av1_get_order_hint(ctx, refs_selected[0]);
> + int idx = hantro_av1_get_frame_index(ctx, refs_selected[0]);
There is no AV1_INVALID_IDX check here either.
I think it might be helpful if there is a comment in the cases where idx is always
valid. I don't know enough about this to be able to tell if it is a potential
out-of-bounds access or not.
> u32 *oh = av1_dec->frame_refs[idx].order_hints;
> int val;
>
> hantro_reg_write(vpu, &av1_use_temporal0_mvs, 1);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_last_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_last2_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_last3_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_golden_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_bwdref_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_altref2_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
> hantro_reg_write(vpu, &av1_mf1_altref_offset, val);
> }
>
> @@ -1776,32 +1005,32 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> if (use_ref_frame_mvs && ref_ind > 1 &&
> cur_offset[mf_types[1] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
> cur_offset[mf_types[1] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
> - int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[1]);
> - int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[1]);
> + int rf = hantro_av1_get_order_hint(ctx, refs_selected[1]);
> + int idx = hantro_av1_get_frame_index(ctx, refs_selected[1]);
Ditto.
> u32 *oh = av1_dec->frame_refs[idx].order_hints;
> int val;
>
> hantro_reg_write(vpu, &av1_use_temporal1_mvs, 1);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_last_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_last2_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_last3_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_golden_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_bwdref_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_altref2_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
> hantro_reg_write(vpu, &av1_mf2_altref_offset, val);
> }
>
> @@ -1816,32 +1045,32 @@ static void rockchip_vpu981_av1_dec_set_other_frames(struct hantro_ctx *ctx)
> if (use_ref_frame_mvs && ref_ind > 2 &&
> cur_offset[mf_types[2] - V4L2_AV1_REF_LAST_FRAME] <= MAX_FRAME_DISTANCE &&
> cur_offset[mf_types[2] - V4L2_AV1_REF_LAST_FRAME] >= -MAX_FRAME_DISTANCE) {
> - int rf = rockchip_vpu981_get_order_hint(ctx, refs_selected[2]);
> - int idx = rockchip_vpu981_get_frame_index(ctx, refs_selected[2]);
> + int rf = hantro_av1_get_order_hint(ctx, refs_selected[2]);
> + int idx = hantro_av1_get_frame_index(ctx, refs_selected[2]);
Ditto.
> u32 *oh = av1_dec->frame_refs[idx].order_hints;
> int val;
>
> hantro_reg_write(vpu, &av1_use_temporal2_mvs, 1);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_last_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST2_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_last2_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_LAST3_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_last3_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_GOLDEN_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_golden_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_BWDREF_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_bwdref_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF2_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_altref2_offset, val);
>
> - val = rockchip_vpu981_av1_dec_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
> + val = hantro_av1_get_dist(ctx, rf, oh[V4L2_AV1_REF_ALTREF_FRAME]);
> hantro_reg_write(vpu, &av1_mf3_altref_offset, val);
> }
>
> @@ -1883,7 +1112,7 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
>
> if (!allow_intrabc) {
> for (i = 0; i < V4L2_AV1_REFS_PER_FRAME; i++) {
> - int idx = rockchip_vpu981_get_frame_index(ctx, i);
> + int idx = hantro_av1_get_frame_index(ctx, i);
>
> if (idx >= 0)
> ref_count[idx]++;
> @@ -1898,7 +1127,7 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
> }
> hantro_reg_write(vpu, &av1_ref_frames, ref_frames);
>
> - rockchip_vpu981_av1_dec_set_frame_sign_bias(ctx);
> + hantro_av1_set_frame_sign_bias(ctx);
>
> for (i = V4L2_AV1_REF_LAST_FRAME; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++) {
> u32 ref = i - 1;
> @@ -1910,8 +1139,8 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
> width = frame->frame_width_minus_1 + 1;
> height = frame->frame_height_minus_1 + 1;
> } else {
> - if (rockchip_vpu981_get_frame_index(ctx, ref) > 0)
> - idx = rockchip_vpu981_get_frame_index(ctx, ref);
> + if (hantro_av1_get_frame_index(ctx, ref) > 0)
> + idx = hantro_av1_get_frame_index(ctx, ref);
Can idx be AV1_INVALID_IDX here?
> width = av1_dec->frame_refs[idx].width;
> height = av1_dec->frame_refs[idx].height;
> }
> @@ -1943,20 +1172,6 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx)
> rockchip_vpu981_av1_dec_set_other_frames(ctx);
> }
>
> -static int rockchip_vpu981_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode)
> -{
> - switch (tx_mode) {
> - case V4L2_AV1_TX_MODE_ONLY_4X4:
> - return ROCKCHIP_AV1_TX_MODE_ONLY_4X4;
> - case V4L2_AV1_TX_MODE_LARGEST:
> - return ROCKCHIP_AV1_TX_MODE_32x32;
> - case V4L2_AV1_TX_MODE_SELECT:
> - return ROCKCHIP_AV1_TX_MODE_SELECT;
> - }
> -
> - return ROCKCHIP_AV1_TX_MODE_32x32;
> -}
> -
> static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
> {
> struct hantro_dev *vpu = ctx->dev;
> @@ -2029,7 +1244,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
> hantro_reg_write(vpu, &av1_comp_pred_mode,
> (ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT) ? 2 : 0);
>
> - tx_mode = rockchip_vpu981_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
> + tx_mode = hantro_av1_get_hardware_tx_mode(ctrls->frame->tx_mode);
> hantro_reg_write(vpu, &av1_transform_mode, tx_mode);
> hantro_reg_write(vpu, &av1_max_cb_size,
> (ctrls->sequence->flags
> @@ -2061,7 +1276,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx)
> hantro_reg_write(vpu, &av1_qmlevel_v, 0xff);
> }
>
> - hantro_reg_write(vpu, &av1_lossless_e, rockchip_vpu981_av1_dec_is_lossless(ctx));
> + hantro_reg_write(vpu, &av1_lossless_e, hantro_av1_is_lossless(ctx));
> hantro_reg_write(vpu, &av1_quant_delta_v_dc, ctrls->frame->quantization.delta_q_v_dc);
> hantro_reg_write(vpu, &av1_quant_delta_v_ac, ctrls->frame->quantization.delta_q_v_ac);
>
> @@ -2109,8 +1324,8 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx)
> struct hantro_decoded_buffer *dst;
> struct vb2_v4l2_buffer *vb2_dst;
> dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
> - size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx);
> - size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx);
> + size_t cr_offset = hantro_av1_luma_size(ctx);
> + size_t mv_offset = hantro_av1_chroma_size(ctx);
>
> vb2_dst = av1_dec->frame_refs[av1_dec->current_frame_index].vb2_ref;
> dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
> @@ -2134,7 +1349,7 @@ int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
>
> hantro_start_prepare_run(ctx);
>
> - ret = rockchip_vpu981_av1_dec_prepare_run(ctx);
> + ret = hantro_av1_prepare_run(ctx);
> if (ret)
> goto prepare_error;
>
> @@ -2144,8 +1359,8 @@ int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
> goto prepare_error;
> }
>
> - rockchip_vpu981_av1_dec_clean_refs(ctx);
> - rockchip_vpu981_av1_dec_frame_ref(ctx, vb2_src->vb2_buf.timestamp);
> + hantro_av1_clean_refs(ctx);
> + hantro_av1_frame_ref(ctx, vb2_src->vb2_buf.timestamp);
>
> rockchip_vpu981_av1_dec_set_parameters(ctx);
> rockchip_vpu981_av1_dec_set_global_model(ctx);
Regards,
Hans
^ permalink raw reply
* [PATCH v4 2/3] dt-bindings: gpio: Add EIO GPIO compatible to gpio-zynq
From: Shubhrajyoti Datta @ 2026-05-12 6:08 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
In-Reply-To: <20260512060917.2096456-1-shubhrajyoti.datta@amd.com>
EIO (Extended IO) GPIO is a Xilinx IP block that exposes
multiplexed I/O pins through an EIO interface.
The EIO GPIO block has 2 banks with 26 GPIOs each (52 total).
The GPIO width cannot be determined from the hardware registers,
the driver relies on the compatible string to select the correct
bank/pin configuration. A new compatible is therefore required.
The block is currently present on xa2ve3288 silicon.
The compatible string uses version 1.0 matching the IP core version.
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
Changes in v4:
- Remove the min 52 for eio
Changes in v3:
- Update the commit message
Changes in v2:
- Add description of EIO block
.../devicetree/bindings/gpio/gpio-zynq.yaml | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
index 30a7f836c341..de24bb361e9f 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
@@ -12,6 +12,7 @@ maintainers:
properties:
compatible:
enum:
+ - xlnx,eio-gpio-1.0
- xlnx,pmc-gpio-1.0
- xlnx,versal-gpio-1.0
- xlnx,zynq-gpio-1.0
@@ -30,7 +31,7 @@ properties:
gpio-line-names:
description: strings describing the names of each gpio line
- minItems: 58
+ minItems: 52
maxItems: 174
interrupt-controller: true
@@ -89,6 +90,16 @@ allOf:
minItems: 116
maxItems: 116
+ - if:
+ properties:
+ compatible:
+ enum:
+ - xlnx,eio-gpio-1.0
+ then:
+ properties:
+ gpio-line-names:
+ maxItems: 52
+
required:
- compatible
- reg
--
2.34.1
^ permalink raw reply related
* [PATCH v4 3/3] gpio: zynq: Add eio gpio support
From: Shubhrajyoti Datta @ 2026-05-12 6:08 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
In-Reply-To: <20260512060917.2096456-1-shubhrajyoti.datta@amd.com>
Add support for the EIO GPIO controller found on
xa2ve3288 silicon.
The EIO GPIO block provides access to multiplexed I/O pins exposed
through the EIO interface. Only bank 0 and bank 1 are connected to
external MIO pins, with 26 GPIOs per bank (52 GPIOs total). This
change extends the Zynq GPIO driver to support the EIO GPIO
variant.
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
(no changes since v1)
drivers/gpio/gpio-zynq.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 571e366624d2..8118ae3412c2 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -25,6 +25,7 @@
#define VERSAL_GPIO_MAX_BANK 4
#define PMC_GPIO_MAX_BANK 5
#define VERSAL_UNUSED_BANKS 2
+#define EIO_GPIO_MAX_BANK 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
@@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
};
+static const struct zynq_platform_data eio_gpio_def = {
+ .label = "eio_gpio",
+ .ngpio = 52,
+ .max_bank = EIO_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
+ .bank_min[1] = 26,
+ .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */
+};
+
static const struct zynq_platform_data versal_gpio_def = {
.label = "versal_gpio",
.quirks = GPIO_QUIRK_VERSAL,
@@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
{ .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def },
{ .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def },
+ { .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 0/3] gpio: Add EIO GPIO support
From: Shubhrajyoti Datta @ 2026-05-12 6:08 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
Add the EIO GPIO support.
Add the dt description and the compatible to the driver.
Changes in v4:
- Add Conor ack
- Remove the min 52 for eio
Changes in v3:
- Update the commit message
Changes in v2:
- Add new patch to sort the compatible strings alphabetically
- Add description of EIO block
Shubhrajyoti Datta (3):
dt-bindings: gpio: zynq: Sort compatible strings alphabetically
dt-bindings: gpio: Add EIO GPIO compatible to gpio-zynq
gpio: zynq: Add eio gpio support
.../devicetree/bindings/gpio/gpio-zynq.yaml | 17 ++++++++++++++---
drivers/gpio/gpio-zynq.c | 12 ++++++++++++
2 files changed, 26 insertions(+), 3 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH v4 1/3] dt-bindings: gpio: zynq: Sort compatible strings alphabetically
From: Shubhrajyoti Datta @ 2026-05-12 6:08 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
In-Reply-To: <20260512060917.2096456-1-shubhrajyoti.datta@amd.com>
Sort the compatible string alphabetically.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
Changes in v4:
- Add Conor ack
Changes in v2:
- Add new patch to sort the compatible strings alphabetically
Documentation/devicetree/bindings/gpio/gpio-zynq.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
index 5e2496379a3c..30a7f836c341 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
@@ -12,10 +12,10 @@ maintainers:
properties:
compatible:
enum:
+ - xlnx,pmc-gpio-1.0
+ - xlnx,versal-gpio-1.0
- xlnx,zynq-gpio-1.0
- xlnx,zynqmp-gpio-1.0
- - xlnx,versal-gpio-1.0
- - xlnx,pmc-gpio-1.0
reg:
maxItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 2/2] net: ti: icssg: Add HSR and LRE PA statistics
From: MD Danish Anwar @ 2026-05-12 6:06 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, MD Danish Anwar,
Roger Quadros, Andrew Lunn, Jacob Keller, Meghana Malladi,
David Carlier, Kevin Hao, Vadim Fedorenko
Cc: netdev, linux-doc, linux-kernel, linux-arm-kernel,
Vignesh Raghavendra
In-Reply-To: <20260512060627.3781329-1-danishanwar@ti.com>
Add new firmware PA statistics counters for HSR and LRE to the ethtool
statistics exposed by the ICSSG driver.
New statistics added:
- FW_HSR_FWD_CHECK_FAIL_DROP: Packets dropped on the HSR forwarding path
- FW_HSR_HE_CHECK_FAIL_DROP: Packets dropped on the HSR host egress path
- FW_HSR_SKIP_HOST_DUP_DISCARD_FRAMES: Frames with duplicate discard
skipped
- FW_LRE_CNT_UNIQUE/DUPLICATE/MULTIPLE_RX: LRE duplicate detetcion
counters
- FW_LRE_CNT_RX/TX: LRE per-port frame counters
- FW_LRE_CNT_OWN_RX: Own HSR tagged frames received
- FW_LRE_CNT_ERRWRONGLAN: Frames with wrong LAN identifier (PRP)
Document the new HSR/LRE statistics in icssg_prueth.rst.
Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
.../device_drivers/ethernet/ti/icssg_prueth.rst | 10 ++++++++++
drivers/net/ethernet/ti/icssg/icssg_common.c | 7 +++++--
drivers/net/ethernet/ti/icssg/icssg_stats.h | 10 ++++++++++
drivers/net/ethernet/ti/icssg/icssg_switch_map.h | 10 ++++++++++
4 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/ti/icssg_prueth.rst b/Documentation/networking/device_drivers/ethernet/ti/icssg_prueth.rst
index da21ddf431bb..b0bda7327b2a 100644
--- a/Documentation/networking/device_drivers/ethernet/ti/icssg_prueth.rst
+++ b/Documentation/networking/device_drivers/ethernet/ti/icssg_prueth.rst
@@ -54,3 +54,13 @@ These statistics are as follows,
- ``FW_HOST_TX_PKT_CNT``: Number of valid packets copied by RTU0 to Tx queues
- ``FW_HOST_EGRESS_Q_PRE_OVERFLOW``: Host Egress Q (Pre-emptible) Overflow Counter
- ``FW_HOST_EGRESS_Q_EXP_OVERFLOW``: Host Egress Q (Pre-emptible) Overflow Counter
+ - ``FW_HSR_FWD_CHECK_FAIL_DROP``: Packets dropped on the HSR forwarding path due to failed checks
+ - ``FW_HSR_HE_CHECK_FAIL_DROP``: Packets dropped on the host egress path due to failed checks
+ - ``FW_HSR_SKIP_HOST_DUP_DISCARD_FRAMES``: Frames for which the host duplicate discard check was skipped
+ - ``FW_LRE_CNT_UNIQUE_RX``: Number of frames received with no duplicate detected
+ - ``FW_LRE_CNT_DUPLICATE_RX``: Number of frames received for which exactly one duplicate was detected
+ - ``FW_LRE_CNT_MULTIPLE_RX``: Number of frames received for which more than one duplicate was detected
+ - ``FW_LRE_CNT_RX``: Number of HSR/PRP tagged frames received
+ - ``FW_LRE_CNT_TX``: Number of HSR/PRP tagged frames sent
+ - ``FW_LRE_CNT_OWN_RX``: Number of HSR/PRP tagged frames received whose source MAC matches the node's own address
+ - ``FW_LRE_CNT_ERRWRONGLAN``: Number of frames received with a wrong LAN identifier, PRP only
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index a28a608f9bf4..e7a51a9eee24 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -1633,7 +1633,8 @@ void icssg_ndo_get_stats64(struct net_device *ndev,
emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") +
emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") +
emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") +
- emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN");
+ emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN") +
+ emac_get_stat_by_name(emac, "FW_LRE_CNT_ERRWRONGLAN");
stats->rx_dropped = ndev->stats.rx_dropped +
emac_get_stat_by_name(emac, "FW_DROPPED_PKT") +
emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") +
@@ -1643,7 +1644,9 @@ void icssg_ndo_get_stats64(struct net_device *ndev,
emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") +
emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") +
emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") +
- emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER");
+ emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER") +
+ emac_get_stat_by_name(emac, "FW_HSR_FWD_CHECK_FAIL_DROP") +
+ emac_get_stat_by_name(emac, "FW_HSR_HE_CHECK_FAIL_DROP");
stats->tx_errors = ndev->stats.tx_errors;
stats->tx_dropped = ndev->stats.tx_dropped +
emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") +
diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.h b/drivers/net/ethernet/ti/icssg/icssg_stats.h
index b854eb587c1e..af3fcecac403 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_stats.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_stats.h
@@ -204,6 +204,16 @@ static const struct icssg_pa_stats icssg_all_pa_stats[] = {
ICSSG_PA_STATS(FW_HOST_TX_PKT_CNT),
ICSSG_PA_STATS(FW_HOST_EGRESS_Q_PRE_OVERFLOW),
ICSSG_PA_STATS(FW_HOST_EGRESS_Q_EXP_OVERFLOW),
+ ICSSG_PA_STATS(FW_HSR_FWD_CHECK_FAIL_DROP),
+ ICSSG_PA_STATS(FW_HSR_HE_CHECK_FAIL_DROP),
+ ICSSG_PA_STATS(FW_HSR_SKIP_HOST_DUP_DISCARD_FRAMES),
+ ICSSG_PA_STATS(FW_LRE_CNT_UNIQUE_RX),
+ ICSSG_PA_STATS(FW_LRE_CNT_DUPLICATE_RX),
+ ICSSG_PA_STATS(FW_LRE_CNT_MULTIPLE_RX),
+ ICSSG_PA_STATS(FW_LRE_CNT_RX),
+ ICSSG_PA_STATS(FW_LRE_CNT_TX),
+ ICSSG_PA_STATS(FW_LRE_CNT_OWN_RX),
+ ICSSG_PA_STATS(FW_LRE_CNT_ERRWRONGLAN),
};
#endif /* __NET_TI_ICSSG_STATS_H */
diff --git a/drivers/net/ethernet/ti/icssg/icssg_switch_map.h b/drivers/net/ethernet/ti/icssg/icssg_switch_map.h
index 7e053b8af3ec..bd2d54dd7f45 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_switch_map.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_switch_map.h
@@ -266,5 +266,15 @@
#define FW_HOST_TX_PKT_CNT 0x0250
#define FW_HOST_EGRESS_Q_PRE_OVERFLOW 0x0258
#define FW_HOST_EGRESS_Q_EXP_OVERFLOW 0x0260
+#define FW_HSR_FWD_CHECK_FAIL_DROP 0x0500
+#define FW_HSR_HE_CHECK_FAIL_DROP 0x0508
+#define FW_HSR_SKIP_HOST_DUP_DISCARD_FRAMES 0x0510
+#define FW_LRE_CNT_UNIQUE_RX 0x0518
+#define FW_LRE_CNT_DUPLICATE_RX 0x0520
+#define FW_LRE_CNT_MULTIPLE_RX 0x0528
+#define FW_LRE_CNT_RX 0x0530
+#define FW_LRE_CNT_TX 0x0538
+#define FW_LRE_CNT_OWN_RX 0x0540
+#define FW_LRE_CNT_ERRWRONGLAN 0x0548
#endif /* __NET_TI_ICSSG_SWITCH_MAP_H */
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 1/2] net: ti: icssg: Derive stats array lengths from ARRAY_SIZE
From: MD Danish Anwar @ 2026-05-12 6:06 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, MD Danish Anwar,
Roger Quadros, Andrew Lunn, Jacob Keller, Meghana Malladi,
David Carlier, Kevin Hao, Vadim Fedorenko
Cc: netdev, linux-doc, linux-kernel, linux-arm-kernel,
Vignesh Raghavendra
In-Reply-To: <20260512060627.3781329-1-danishanwar@ti.com>
Replace the manually maintained ICSSG_NUM_MIIG_STATS and
ICSSG_NUM_PA_STATS constants with ARRAY_SIZE() expressions derived
directly from the corresponding stat descriptor arrays, so that adding
new entries to icssg_all_miig_stats[] or icssg_all_pa_stats[] no longer
requires a separate update to a numeric constant.
To make this self-contained, break the circular include dependency
between icssg_stats.h and icssg_prueth.h:
- icssg_stats.h previously included icssg_prueth.h (transitively
pulling in icssg_switch_map.h and ETH_GSTRING_LEN). Replace that
with direct includes of <linux/ethtool.h>, <linux/kernel.h> and
"icssg_switch_map.h".
- icssg_prueth.h now includes icssg_stats.h, giving it access to
the ARRAY_SIZE-based ICSSG_NUM_MIIG_STATS and ICSSG_NUM_PA_STATS
before they are used in the prueth_emac struct and ICSSG_NUM_STATS.
Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
drivers/net/ethernet/ti/icssg/icssg_prueth.h | 3 +--
drivers/net/ethernet/ti/icssg/icssg_stats.h | 7 ++++++-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index df93d15c5b78..e2ccecb0a0dd 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -43,6 +43,7 @@
#include "icssg_config.h"
#include "icss_iep.h"
+#include "icssg_stats.h"
#include "icssg_switch_map.h"
#define PRUETH_MAX_MTU (2000 - ETH_HLEN - ETH_FCS_LEN)
@@ -57,8 +58,6 @@
#define ICSSG_MAX_RFLOWS 8 /* per slice */
-#define ICSSG_NUM_PA_STATS 32
-#define ICSSG_NUM_MIIG_STATS 60
/* Number of ICSSG related stats */
#define ICSSG_NUM_STATS (ICSSG_NUM_MIIG_STATS + ICSSG_NUM_PA_STATS)
#define ICSSG_NUM_STANDARD_STATS 31
diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.h b/drivers/net/ethernet/ti/icssg/icssg_stats.h
index 5ec0b38e0c67..b854eb587c1e 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_stats.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_stats.h
@@ -8,10 +8,15 @@
#ifndef __NET_TI_ICSSG_STATS_H
#define __NET_TI_ICSSG_STATS_H
-#include "icssg_prueth.h"
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+#include "icssg_switch_map.h"
#define STATS_TIME_LIMIT_1G_MS 25000 /* 25 seconds @ 1G */
+#define ICSSG_NUM_MIIG_STATS ARRAY_SIZE(icssg_all_miig_stats)
+#define ICSSG_NUM_PA_STATS ARRAY_SIZE(icssg_all_pa_stats)
+
struct miig_stats_regs {
/* Rx */
u32 rx_packets;
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 0/2] Add ICSSG firmware stats related to HSR
From: MD Danish Anwar @ 2026-05-12 6:06 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, MD Danish Anwar,
Roger Quadros, Andrew Lunn, Jacob Keller, Meghana Malladi,
David Carlier, Kevin Hao, Vadim Fedorenko
Cc: netdev, linux-doc, linux-kernel, linux-arm-kernel,
Vignesh Raghavendra
This series has two pacthes,
Patch 1/2 Updates Stats counter to use ARRAY_SIZE instead of hardcoded length.
Patch 2/2 Adds new stats related to HSR / PRP maintained by ICSSG firmware.
MD Danish Anwar (2):
net: ti: icssg: Derive stats array lengths from ARRAY_SIZE
net: ti: icssg: Add HSR and LRE PA statistics
.../device_drivers/ethernet/ti/icssg_prueth.rst | 10 ++++++++++
drivers/net/ethernet/ti/icssg/icssg_common.c | 7 +++++--
drivers/net/ethernet/ti/icssg/icssg_prueth.h | 3 +--
drivers/net/ethernet/ti/icssg/icssg_stats.h | 17 ++++++++++++++++-
.../net/ethernet/ti/icssg/icssg_switch_map.h | 10 ++++++++++
5 files changed, 42 insertions(+), 5 deletions(-)
base-commit: 63751099502d10f0aa6bb35273e56c5800cc4e3a
--
2.34.1
^ permalink raw reply
* RE: [PATCH v6 net-next 15/15] net: dsa: netc: add support for ethtool private statistics
From: Wei Fang @ 2026-05-12 6:00 UTC (permalink / raw)
To: Networking
Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org, imx@lists.linux.dev,
Claudiu Manoil, Vladimir Oltean, Clark Wang,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org, f.fainelli@gmail.com,
Frank Li, chleroy@kernel.org, horms@kernel.org,
linux@armlinux.org.uk, maxime.chevallier@bootlin.com,
andrew@lunn.ch, olteanv@gmail.com
In-Reply-To: <20260509102954.4116624-16-wei.fang@nxp.com>
I noticed that sashiko-nipa aslo reported some warnings, most of them
are minor issues or false positives. I will fix/improve them in the next
version. Only this patch reported a high warning, but I don't think it's
a problem. See sashiko's comment and my reply below.
Link: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260509102954.4116624-1-wei.fang%40nxp.com
> +static const struct netc_port_stat netc_port_counters[] = {
> + { NETC_PTGSLACR, "port gate late arrival frames" },
> + { NETC_PSDFTCR, "port SDF transmit frames" },
> + { NETC_PSDFDDCR, "port SDF drop duplicate frames" },
> + { NETC_PRXDCR, "port rx discard frames" },
> + { NETC_PRXDCRRR, "port rx discard read-reset" },
> + { NETC_PRXDCRR0, "port rx discard reason 0" },
> + { NETC_PRXDCRR1, "port rx discard reason 1" },
> + { NETC_PTXDCR, "port tx discard frames" },
> + { NETC_PTXDCRRR, "port tx discard read-reset" },
> + { NETC_PTXDCRR0, "port tx discard reason 0" },
> + { NETC_PTXDCRR1, "port tx discard reason 1" },
> + { NETC_BPDCR, "bridge port discard frames" },
> + { NETC_BPDCRRR, "bridge port discard read-reset" },
> + { NETC_BPDCRR0, "bridge port discard reason 0" },
> + { NETC_BPDCRR1, "bridge port discard reason 1" },
> +};
Should the port rx/tx discard frame counts and the bridge port discard
frame count be exposed through ethtool -S at all? These look like values
that map onto the standard rtnl_link_stats64 fields (rx_dropped /
tx_dropped) reported via ndo_get_stats64 and visible through "ip -s link
show" on the DSA user netdev.
[Wei Fang] The port rx/tx discard frame counts and the bridge port discard
frame count are used to count the number of packets lost due to certain
reasons including buffer exhaustion. By querying the packet loss reason
register, users can clearly understand why the frames are dropped. These
counters are not the same concept as rx_dropped/tx_dropped in
rtnl_link_stats64. For example, rx_dropped should not includes packets
dropped by the device due to buffer exhaustion, but PRXDCR includes them.
Documentation/networking/statistics.rst calls out that ethtool -S is for
driver-private counters only, and the networking review guidance asks
that stats which already have a standard uAPI not be duplicated under
ethtool -S even when the driver is not currently feeding the standard
interface. Could the discard frame totals be routed through
ndo_get_stats64 instead, leaving only the truly private parts (the
per-reason RR0/RR1 codes, the SDF counters, the gate late-arrival
counter) under ethtool -S?
[Wei Fang] As I explained above, I would like to keep them in the
driver-private counters.
Is including NETC_PRXDCRRR / NETC_PTXDCRRR / NETC_BPDCRRR in the routine
ethtool -S read path intentional? The "RR" suffix on these registers
[Wei Fang] Yes, for example, NETC_PRXDCR represents the total statistics,
while NETC_PRXDCRRR represents the statistics between the last read and
the current read. During user debugging, this register, along with the packet
drop reason register, can be used to determine the cause of packet drop
during this period.
appears to denote read-reset semantics; the sibling ENETC4 driver
documents the same naming convention, e.g. in
drivers/net/ethernet/freescale/enetc/enetc4_hw.h:
/* Port Rx discard count read-reset register */
... ENETC4_PRXDCRRR ...
netc_port_get_ethtool_stats() reads them on every invocation:
for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
*data++ = netc_port_rd(np, netc_port_counters[i].reg);
If reading PRXDCRRR/PTXDCRRR/BPDCRRR clears the underlying counter as a
side effect, then "watch ethtool -S" or any periodic poll will see a
delta-since-last-read value rather than a cumulative total, and two
concurrent observers will perturb each other's view. Would it be safer
to expose only the non-reset variants (PRXDCR/PTXDCR/BPDCR, which are
already in this list as "port rx/tx discard frames" and "bridge port
discard frames") here, and move the read-reset aliases to a one-shot
debug interface such as devlink or debugfs?
[Wei Fang] Currently, we do not support devlink and debugfs, which
makes debugging packet drop issues quite cumbersome for users.
This current approach can serve as a transitional solution; we can
remove it from ethtool -S when debugfs or devlink are supported.
> +
> +static const struct netc_port_stat netc_emac_counters[] = {
> + { NETC_PM_ROCT(0), "eMAC rx octets" },
> + { NETC_PM_RVLAN(0), "eMAC rx VLAN frames" },
> + { NETC_PM_RERR(0), "eMAC rx frame errors" },
> + { NETC_PM_RUCA(0), "eMAC rx unicast frames" },
> + { NETC_PM_RDRP(0), "eMAC rx dropped packets" },
> + { NETC_PM_RPKT(0), "eMAC rx packets" },
> + { NETC_PM_TOCT(0), "eMAC tx octets" },
> + { NETC_PM_TVLAN(0), "eMAC tx VLAN frames" },
> + { NETC_PM_TFCS(0), "eMAC tx FCS errors" },
> + { NETC_PM_TUCA(0), "eMAC tx unicast frames" },
> + { NETC_PM_TPKT(0), "eMAC tx packets" },
> + { NETC_PM_TUND(0), "eMAC tx undersized packets" },
> + { NETC_PM_TIOCT(0), "eMAC tx invalid octets" },
> +};
> +
> +static const struct netc_port_stat netc_pmac_counters[] = {
> + { NETC_PM_ROCT(1), "pMAC rx octets" },
> + { NETC_PM_RVLAN(1), "pMAC rx VLAN frames" },
> + { NETC_PM_RERR(1), "pMAC rx frame errors" },
> + { NETC_PM_RUCA(1), "pMAC rx unicast frames" },
> + { NETC_PM_RDRP(1), "pMAC rx dropped packets" },
> + { NETC_PM_RPKT(1), "pMAC rx packets" },
> + { NETC_PM_TOCT(1), "pMAC tx octets" },
> + { NETC_PM_TVLAN(1), "pMAC tx VLAN frames" },
> + { NETC_PM_TFCS(1), "pMAC tx FCS errors" },
> + { NETC_PM_TUCA(1), "pMAC tx unicast frames" },
> + { NETC_PM_TPKT(1), "pMAC tx packets" },
> + { NETC_PM_TUND(1), "pMAC tx undersized packets" },
> + { NETC_PM_TIOCT(1), "pMAC tx invalid octets" },
> +};
Do most of these eMAC and pMAC strings duplicate counters that already
have a standard uAPI?
[Wei Fang] No, the standard uAPI is implemented in patch 14. This part of
the statistics is not supported by the standard API.
Several look like direct overlaps with struct ethtool_eth_mac_stats,
which this driver already populates via .get_eth_mac_stats:
eMAC/pMAC rx octets (NETC_PM_ROCT) vs OctetsReceivedOK
(already exposed via NETC_PM_REOCT)
eMAC/pMAC tx octets (NETC_PM_TOCT) vs OctetsTransmittedOK
(already exposed via NETC_PM_TEOCT)
eMAC/pMAC rx packets (NETC_PM_RPKT) vs FramesReceivedOK (NETC_PM_RFRM)
eMAC/pMAC tx packets (NETC_PM_TPKT) vs FramesTransmittedOK (NETC_PM_TFRM)
eMAC/pMAC tx FCS errors / rx frame errors
vs FrameCheckSequenceErrors and the
FramesLostDueToIntMAC*Error fields
[Wei Fang] They are different registers and the statistical data they
produce is different. In addition, FrameCheckSequenceErrors is used
for RX not TX.
Others overlap rtnl_link_stats64 (visible via "ip -s link show"):
rx octets / tx octets -> rx_bytes / tx_bytes
rx packets / tx packets -> rx_packets / tx_packets
rx dropped packets (RDRP) -> rx_dropped
rx/tx unicast frames -> derivable from rtnl_link_stats64's
total/multicast/broadcast triplet
[Wei Fang] As I explained in v5, rtnl_link_stats64 is used to collect total
statistics for the network device. For NETC switch, its ports support
preemption, so each port has two MACs, the netc_p/emac_counters is
used to collect statistics for each MAC. Their usage are different.
And tx undersized packets (NETC_PM_TUND) overlaps the rmon undersize
buckets that .get_rmon_stats already reports.
[Wei Fang] The undersize_pkts of struct ethtool_rmon_stats is used for RX
not TX.
^ permalink raw reply
* [PATCH v2] perf record: Refactor ARM64 leaf caller setup out of arch
From: Ian Rogers @ 2026-05-12 5:41 UTC (permalink / raw)
To: acme, james.clark, namhyung
Cc: adrian.hunter, alexander.shishkin, dapeng1.mi, john.g.garry,
jolsa, leo.yan, linux-arm-kernel, linux-kernel, linux-perf-users,
mark.rutland, mike.leach, mingo, peterz, shimin.guo, will,
Ian Rogers
In-Reply-To: <20260507170622.611933-1-irogers@google.com>
Code in tools/perf/arch causes portability issues/opaqueness and LTO
issues due to the use of weak symbols. Move the adding of LR to the
sample_user_regs into arm64-frame-pointer-unwind-support.c conditional
on EM_HOST == EM_AARCH64 (false on all non-ARM64 builds). This also
better encapsulates the use of the sampled registers by
get_leaf_frame_caller_aarch64 and the set up by the new
add_leaf_frame_caller_opts_aarch64, exposing opportunities for
possibly sampling PC and SP to help the unwinder.
Reviewed-by: James Clark <james.clark@linaro.org>
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/machine.c | 12 ------------
tools/perf/builtin-record.c | 11 +++++------
tools/perf/util/arm64-frame-pointer-unwind-support.c | 6 ++++++
tools/perf/util/arm64-frame-pointer-unwind-support.h | 2 ++
tools/perf/util/callchain.h | 2 --
6 files changed, 13 insertions(+), 21 deletions(-)
delete mode 100644 tools/perf/arch/arm64/util/machine.c
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
index 4e06a08d281a..638aa6948ab5 100644
--- a/tools/perf/arch/arm64/util/Build
+++ b/tools/perf/arch/arm64/util/Build
@@ -5,7 +5,6 @@ perf-util-y += ../../arm/util/pmu.o
perf-util-y += arm-spe.o
perf-util-y += header.o
perf-util-y += hisi-ptt.o
-perf-util-y += machine.o
perf-util-y += mem-events.o
perf-util-y += pmu.o
perf-util-y += tsc.o
diff --git a/tools/perf/arch/arm64/util/machine.c b/tools/perf/arch/arm64/util/machine.c
deleted file mode 100644
index 80fb13c958d9..000000000000
--- a/tools/perf/arch/arm64/util/machine.c
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include "callchain.h" // prototype of arch__add_leaf_frame_record_opts
-#include "perf_regs.h"
-#include "record.h"
-
-#define SMPL_REG_MASK(b) (1ULL << (b))
-
-void arch__add_leaf_frame_record_opts(struct record_opts *opts)
-{
- opts->sample_user_regs |= SMPL_REG_MASK(PERF_REG_ARM64_LR);
-}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4a5eba498c02..272bba7f4b9e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -14,6 +14,7 @@
#include "util/parse-events.h"
#include "util/config.h"
+#include "util/arm64-frame-pointer-unwind-support.h"
#include "util/callchain.h"
#include "util/cgroup.h"
#include "util/header.h"
@@ -3230,10 +3231,6 @@ static int record__parse_off_cpu_thresh(const struct option *opt,
return 0;
}
-void __weak arch__add_leaf_frame_record_opts(struct record_opts *opts __maybe_unused)
-{
-}
-
static int parse_control_option(const struct option *opt,
const char *str,
int unset __maybe_unused)
@@ -4319,8 +4316,10 @@ int cmd_record(int argc, const char **argv)
evlist__warn_user_requested_cpus(rec->evlist, rec->opts.target.cpu_list);
- if (callchain_param.enabled && callchain_param.record_mode == CALLCHAIN_FP)
- arch__add_leaf_frame_record_opts(&rec->opts);
+ if (callchain_param.enabled && callchain_param.record_mode == CALLCHAIN_FP) {
+ if (EM_HOST == EM_AARCH64)
+ add_leaf_frame_caller_opts_aarch64(&rec->opts);
+ }
err = -ENOMEM;
if (evlist__create_maps(rec->evlist, &rec->opts.target) < 0) {
diff --git a/tools/perf/util/arm64-frame-pointer-unwind-support.c b/tools/perf/util/arm64-frame-pointer-unwind-support.c
index 858ce2b01812..3af8c7a466e0 100644
--- a/tools/perf/util/arm64-frame-pointer-unwind-support.c
+++ b/tools/perf/util/arm64-frame-pointer-unwind-support.c
@@ -2,6 +2,7 @@
#include "arm64-frame-pointer-unwind-support.h"
#include "callchain.h"
#include "event.h"
+#include "record.h"
#include "unwind.h"
#include <string.h>
@@ -16,6 +17,11 @@ struct entries {
#define SMPL_REG_MASK(b) (1ULL << (b))
+void add_leaf_frame_caller_opts_aarch64(struct record_opts *opts)
+{
+ opts->sample_user_regs |= SMPL_REG_MASK(PERF_REG_ARM64_LR);
+}
+
static bool get_leaf_frame_caller_enabled(struct perf_sample *sample)
{
struct regs_dump *regs;
diff --git a/tools/perf/util/arm64-frame-pointer-unwind-support.h b/tools/perf/util/arm64-frame-pointer-unwind-support.h
index 42d3a45490f5..ba35b295bfcd 100644
--- a/tools/perf/util/arm64-frame-pointer-unwind-support.h
+++ b/tools/perf/util/arm64-frame-pointer-unwind-support.h
@@ -5,8 +5,10 @@
#include <linux/types.h>
struct perf_sample;
+struct record_opts;
struct thread;
+void add_leaf_frame_caller_opts_aarch64(struct record_opts *opts);
u64 get_leaf_frame_caller_aarch64(struct perf_sample *sample, struct thread *thread, int user_idx);
#endif /* __PERF_ARM_FRAME_POINTER_UNWIND_SUPPORT_H */
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 06d463ccc7a0..b7702d65ad60 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -277,8 +277,6 @@ static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused,
}
#endif
-void arch__add_leaf_frame_record_opts(struct record_opts *opts);
-
char *callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize, bool show_dso);
char *callchain_node__scnprintf_value(struct callchain_node *node,
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* Re: Re: Re: [PATCH net-next v7 2/4] net: stmmac: eic7700: enable clocks before syscon access and correct RX sampling timing
From: 李志 @ 2026-05-12 5:39 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew+netdev, devicetree, davem, edumazet, robh, krzk+dt,
conor+dt, netdev, pabeni, mcoquelin.stm32, alexandre.torgue,
rmk+kernel, pjw, palmer, aou, alex, linux-riscv, linux-stm32,
linux-arm-kernel, linux-kernel, maxime.chevallier, ningyu, linmin,
pinkesh.vaghela, pritesh.patel, weishangjuan, horms
In-Reply-To: <38d052b7.7a70.19dfb0d782b.Coremail.lizhi2@eswincomputing.com>
> -----Original Messages-----
> From: 李志 <lizhi2@eswincomputing.com>
> Send time:Wednesday, 06/05/2026 10:10:57
> To: "Jakub Kicinski" <kuba@kernel.org>
> Cc: andrew+netdev@lunn.ch, devicetree@vger.kernel.org, davem@davemloft.net, edumazet@google.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, netdev@vger.kernel.org, pabeni@redhat.com, mcoquelin.stm32@gmail.com, alexandre.torgue@foss.st.com, rmk+kernel@armlinux.org.uk, pjw@kernel.org, palmer@dabbelt.com, aou@eecs.berkeley.edu, alex@ghiti.fr, linux-riscv@lists.infradead.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, maxime.chevallier@bootlin.com, ningyu@eswincomputing.com, linmin@eswincomputing.com, pinkesh.vaghela@einfochips.com, pritesh.patel@einfochips.com, weishangjuan@eswincomputing.com, horms@kernel.org
> Subject: Re: Re: [PATCH net-next v7 2/4] net: stmmac: eic7700: enable clocks before syscon access and correct RX sampling timing
>
>
>
>
> > -----原始邮件-----
> > 发件人: "Jakub Kicinski" <kuba@kernel.org>
> > 发送时间:2026-05-01 07:35:51 (星期五)
> > 收件人: 李志 <lizhi2@eswincomputing.com>
> > 抄送: andrew+netdev@lunn.ch, devicetree@vger.kernel.org, davem@davemloft.net, edumazet@google.com, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, netdev@vger.kernel.org, pabeni@redhat.com, mcoquelin.stm32@gmail.com, alexandre.torgue@foss.st.com, rmk+kernel@armlinux.org.uk, pjw@kernel.org, palmer@dabbelt.com, aou@eecs.berkeley.edu, alex@ghiti.fr, linux-riscv@lists.infradead.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, maxime.chevallier@bootlin.com, ningyu@eswincomputing.com, linmin@eswincomputing.com, pinkesh.vaghela@einfochips.com, pritesh.patel@einfochips.com, weishangjuan@eswincomputing.com, horms@kernel.org
> > 主题: Re: [PATCH net-next v7 2/4] net: stmmac: eic7700: enable clocks before syscon access and correct RX sampling timing
> >
> > On Thu, 30 Apr 2026 14:43:50 +0800 (GMT+08:00) 李志 wrote:
> > > > Why Fixes? If eth1 never worked this is not a fix but new functionality
> > > > If you want to make this a fix to prevent incompatibility - cut it down
> > > > just to the eth0 changes.
> > > >
> > > Thank you for the suggestion.
> > >
> > > You're right that eth1 never worked at Gigabit speed, so this should
> > > not be treated as a fix.
> > >
> > > In v8, I will split the changes into two patches within the same series:
> > > - Patch 1 will contain only the fixes affecting the existing eth0
> > > functionality, and will keep the Fixes tag.
> > > - Patch 2 will add the eth1 support (RX clock inversion workaround)
> > > as new functionality, without a Fixes tag.
> > >
> > > Please let me know if you would prefer a different split or ordering.
> >
> > If you want to consider some part of this commit a fix it has to be
> > posted separately to the net tree (rather than net-next).
> > Once it's merged and makes it way over to the net-next tree (each
> > Thursday) you can post the net-next chnages for eth1
>
> Thanks, understood.
>
> I will split the changes accordingly:
> - Send the eth0 fixes as a new v1 series targeting net.
> - Send the eth1 enablement as a new v1 series targeting net-next.
>
> Thanks for the guidance.
Hi Jakub,
Just to confirm the versioning for the eth1 part after splitting the
original v7/v8 series:
For the net fix part, I have already posted it as a new v1 series
targeting net.
For the eth1 enablement part, my current understanding is that it
should be treated as a new independent v1 series for net-next,
since the scope and target tree have changed after the split.
Would you prefer this eth1 series to start as v1, or should it
continue as v8 for continuity with the original series?
I will follow your preferred approach before posting it.
Thanks,
Zhi
^ permalink raw reply
* [PATCH 10/19] raid6: rework the init helpers
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
Match the xor version with two initcalls for the built-in case to delay
calibrartion. This prepares for adding non-calibration init code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/Kconfig | 11 +
lib/raid/raid6/Makefile | 4 +
lib/raid/raid6/algos.c | 309 ++++++++----------
lib/raid/raid6/algos.h | 67 +---
lib/raid/raid6/arm/neon.c | 6 -
lib/raid/raid6/arm/pq_arch.h | 24 ++
lib/raid/raid6/arm/recov_neon.c | 7 -
lib/raid/raid6/arm64/pq_arch.h | 1 +
lib/raid/raid6/loongarch/loongarch_simd.c | 12 -
lib/raid/raid6/loongarch/pq_arch.h | 23 ++
.../raid6/loongarch/recov_loongarch_simd.c | 14 -
lib/raid/raid6/powerpc/altivec.uc | 10 -
lib/raid/raid6/powerpc/pq_arch.h | 31 ++
lib/raid/raid6/powerpc/vpermxor.uc | 11 -
lib/raid/raid6/recov.c | 2 -
lib/raid/raid6/riscv/pq_arch.h | 21 ++
lib/raid/raid6/riscv/recov_rvv.c | 2 -
lib/raid/raid6/riscv/rvv.h | 6 -
lib/raid/raid6/s390/pq_arch.h | 15 +
lib/raid/raid6/s390/recov_s390xc.c | 2 -
lib/raid/raid6/s390/s390vx.uc | 7 -
lib/raid/raid6/tests/raid6_kunit.c | 23 +-
lib/raid/raid6/x86/avx2.c | 14 -
lib/raid/raid6/x86/avx512.c | 19 --
lib/raid/raid6/x86/mmx.c | 8 -
lib/raid/raid6/x86/pq_arch.h | 97 ++++++
lib/raid/raid6/x86/recov_avx2.c | 8 -
lib/raid/raid6/x86/recov_avx512.c | 12 -
lib/raid/raid6/x86/recov_ssse3.c | 9 -
lib/raid/raid6/x86/sse1.c | 12 -
lib/raid/raid6/x86/sse2.c | 15 -
31 files changed, 393 insertions(+), 409 deletions(-)
create mode 100644 lib/raid/raid6/arm/pq_arch.h
create mode 100644 lib/raid/raid6/arm64/pq_arch.h
create mode 100644 lib/raid/raid6/loongarch/pq_arch.h
create mode 100644 lib/raid/raid6/powerpc/pq_arch.h
create mode 100644 lib/raid/raid6/riscv/pq_arch.h
create mode 100644 lib/raid/raid6/s390/pq_arch.h
create mode 100644 lib/raid/raid6/x86/pq_arch.h
diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig
index e39f6d667792..978cd6ba08ac 100644
--- a/lib/raid/Kconfig
+++ b/lib/raid/Kconfig
@@ -32,6 +32,17 @@ config XOR_KUNIT_TEST
config RAID6_PQ
tristate
+# selected by architectures that provide an optimized PQ implementation
+config RAID6_PQ_ARCH
+ depends on RAID6_PQ
+ default y if KERNEL_MODE_NEON # arm32/arm64
+ default y if LOONGARCH
+ default y if ALTIVEC # powerpc
+ default y if RISCV_ISA_V
+ default y if S390
+ default y if X86
+ bool
+
config RAID6_PQ_KUNIT_TEST
tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/raid/raid6/Makefile b/lib/raid/raid6/Makefile
index f64f6d32f28b..ed1f512b88af 100644
--- a/lib/raid/raid6/Makefile
+++ b/lib/raid/raid6/Makefile
@@ -4,6 +4,10 @@ hostprogs += mktables
ccflags-y += -I $(src)
+ifeq ($(CONFIG_RAID6_PQ_ARCH),y)
+CFLAGS_algos.o += -I$(src)/$(SRCARCH)
+endif
+
obj-$(CONFIG_RAID6_PQ) += raid6_pq.o tests/
raid6_pq-y += algos.o tables.o
diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c
index af31a1feb6e7..5bd953032e55 100644
--- a/lib/raid/raid6/algos.c
+++ b/lib/raid/raid6/algos.c
@@ -17,6 +17,9 @@
#include <kunit/visibility.h>
#include "algos.h"
+#define RAID6_MAX_ALGOS 16
+static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS];
+static unsigned int raid6_nr_algos;
static const struct raid6_recov_calls *raid6_recov_algo;
/* Selected algorithm */
@@ -97,71 +100,6 @@ bool raid6_can_xor_syndrome(void)
}
EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
-const struct raid6_calls * const raid6_algos[] = {
-#if defined(__i386__) && !defined(__arch_um__)
- &raid6_avx512x2,
- &raid6_avx512x1,
- &raid6_avx2x2,
- &raid6_avx2x1,
- &raid6_sse2x2,
- &raid6_sse2x1,
- &raid6_sse1x2,
- &raid6_sse1x1,
- &raid6_mmxx2,
- &raid6_mmxx1,
-#endif
-#if defined(__x86_64__) && !defined(__arch_um__)
- &raid6_avx512x4,
- &raid6_avx512x2,
- &raid6_avx512x1,
- &raid6_avx2x4,
- &raid6_avx2x2,
- &raid6_avx2x1,
- &raid6_sse2x4,
- &raid6_sse2x2,
- &raid6_sse2x1,
-#endif
-#ifdef CONFIG_ALTIVEC
- &raid6_vpermxor8,
- &raid6_vpermxor4,
- &raid6_vpermxor2,
- &raid6_vpermxor1,
- &raid6_altivec8,
- &raid6_altivec4,
- &raid6_altivec2,
- &raid6_altivec1,
-#endif
-#if defined(CONFIG_S390)
- &raid6_s390vx8,
-#endif
-#ifdef CONFIG_KERNEL_MODE_NEON
- &raid6_neonx8,
- &raid6_neonx4,
- &raid6_neonx2,
- &raid6_neonx1,
-#endif
-#ifdef CONFIG_LOONGARCH
-#ifdef CONFIG_CPU_HAS_LASX
- &raid6_lasx,
-#endif
-#ifdef CONFIG_CPU_HAS_LSX
- &raid6_lsx,
-#endif
-#endif
-#ifdef CONFIG_RISCV_ISA_V
- &raid6_rvvx1,
- &raid6_rvvx2,
- &raid6_rvvx4,
- &raid6_rvvx8,
-#endif
- &raid6_intx8,
- &raid6_intx4,
- &raid6_intx2,
- &raid6_intx1,
- NULL
-};
-EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
-
/**
* raid6_recov_2data - recover two missing data disks
* @disks: number of "disks" to operate on including parity
@@ -215,119 +153,57 @@ void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
}
EXPORT_SYMBOL_GPL(raid6_recov_datap);
-const struct raid6_recov_calls *const raid6_recov_algos[] = {
-#ifdef CONFIG_X86
- &raid6_recov_avx512,
- &raid6_recov_avx2,
- &raid6_recov_ssse3,
-#endif
-#ifdef CONFIG_S390
- &raid6_recov_s390xc,
-#endif
-#if defined(CONFIG_KERNEL_MODE_NEON)
- &raid6_recov_neon,
-#endif
-#ifdef CONFIG_LOONGARCH
-#ifdef CONFIG_CPU_HAS_LASX
- &raid6_recov_lasx,
-#endif
-#ifdef CONFIG_CPU_HAS_LSX
- &raid6_recov_lsx,
-#endif
-#endif
-#ifdef CONFIG_RISCV_ISA_V
- &raid6_recov_rvv,
-#endif
- &raid6_recov_intx1,
- NULL
-};
-EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
-
#define RAID6_TIME_JIFFIES_LG2 4
#define RAID6_TEST_DISKS 8
#define RAID6_TEST_DISKS_ORDER 3
-static inline const struct raid6_recov_calls *raid6_choose_recov(void)
+static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS],
+ const int disks)
{
- const struct raid6_recov_calls *const *algo;
- const struct raid6_recov_calls *best;
-
- for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
- if (!best || (*algo)->priority > best->priority)
- if (!(*algo)->valid || (*algo)->valid())
- best = *algo;
+ /* work on the second half of the disks */
+ int start = (disks >> 1) - 1, stop = disks - 3;
+ const struct raid6_calls *best = NULL;
+ unsigned long bestgenperf = 0;
+ unsigned int i;
- if (best) {
- raid6_recov_algo = best;
+ for (i = 0; i < raid6_nr_algos; i++) {
+ const struct raid6_calls *algo = raid6_algos[i];
+ unsigned long perf = 0, j0, j1;
- pr_info("raid6: using %s recovery algorithm\n", best->name);
- } else
- pr_err("raid6: Yikes! No recovery algorithm found!\n");
-
- return best;
-}
+ preempt_disable();
+ j0 = jiffies;
+ while ((j1 = jiffies) == j0)
+ cpu_relax();
+ while (time_before(jiffies,
+ j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
+ algo->gen_syndrome(disks, PAGE_SIZE, *dptrs);
+ perf++;
+ }
+ preempt_enable();
-static inline const struct raid6_calls *raid6_choose_gen(
- void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
-{
- unsigned long perf, bestgenperf, j0, j1;
- int start = (disks>>1)-1, stop = disks-3; /* work on the second half of the disks */
- const struct raid6_calls *const *algo;
- const struct raid6_calls *best;
-
- for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
- if (!best || (*algo)->priority >= best->priority) {
- if ((*algo)->valid && !(*algo)->valid())
- continue;
-
- if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
- best = *algo;
- break;
- }
-
- perf = 0;
-
- preempt_disable();
- j0 = jiffies;
- while ((j1 = jiffies) == j0)
- cpu_relax();
- while (time_before(jiffies,
- j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
- (*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);
- perf++;
- }
- preempt_enable();
-
- if (perf > bestgenperf) {
- bestgenperf = perf;
- best = *algo;
- }
- pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name,
- (perf * HZ * (disks-2)) >>
- (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
+ if (perf > bestgenperf) {
+ bestgenperf = perf;
+ best = algo;
}
+ pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name,
+ (perf * HZ * (disks-2)) >>
+ (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
}
if (!best) {
pr_err("raid6: Yikes! No algorithm found!\n");
- goto out;
+ return -EINVAL;
}
raid6_call = *best;
- if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
- pr_info("raid6: skipped pq benchmark and selected %s\n",
- best->name);
- goto out;
- }
-
pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
best->name,
(bestgenperf * HZ * (disks - 2)) >>
(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2));
if (best->xor_syndrome) {
- perf = 0;
+ unsigned long perf = 0, j0, j1;
preempt_disable();
j0 = jiffies;
@@ -346,8 +222,7 @@ static inline const struct raid6_calls *raid6_choose_gen(
(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
}
-out:
- return best;
+ return 0;
}
@@ -357,12 +232,17 @@ static inline const struct raid6_calls *raid6_choose_gen(
static int __init raid6_select_algo(void)
{
const int disks = RAID6_TEST_DISKS;
-
- const struct raid6_calls *gen_best;
- const struct raid6_recov_calls *rec_best;
char *disk_ptr, *p;
void *dptrs[RAID6_TEST_DISKS];
int i, cycle;
+ int error;
+
+ if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) {
+ pr_info("raid6: skipped pq benchmark and selected %s\n",
+ raid6_algos[raid6_nr_algos - 1]->name);
+ raid6_call = *raid6_algos[raid6_nr_algos - 1];
+ return 0;
+ }
/* prepare the buffer and fill it circularly with gfmul table */
disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
@@ -385,22 +265,115 @@ static int __init raid6_select_algo(void)
memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
/* select raid gen_syndrome function */
- gen_best = raid6_choose_gen(&dptrs, disks);
-
- /* select raid recover functions */
- rec_best = raid6_choose_recov();
+ error = raid6_choose_gen(&dptrs, disks);
free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
- return gen_best && rec_best ? 0 : -EINVAL;
+ return error;
+}
+
+void __init raid6_algo_add(const struct raid6_calls *algo)
+{
+ if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS))
+ return;
+ raid6_algos[raid6_nr_algos++] = algo;
+}
+
+void __init raid6_algo_add_default(void)
+{
+ raid6_algo_add(&raid6_intx1);
+ raid6_algo_add(&raid6_intx2);
+ raid6_algo_add(&raid6_intx4);
+ raid6_algo_add(&raid6_intx8);
+}
+
+void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo)
+{
+ if (WARN_ON_ONCE(raid6_recov_algo))
+ return;
+ raid6_recov_algo = algo;
+}
+
+#ifdef CONFIG_RAID6_PQ_ARCH
+#include "pq_arch.h"
+#else
+static inline void arch_raid6_init(void)
+{
+ raid6_algo_add_default();
+}
+#endif /* CONFIG_RAID6_PQ_ARCH */
+
+static int __init raid6_init(void)
+{
+ /*
+ * Architectures providing arch_raid6_init must add all PQ generation
+ * algorithms they want to consider in arch_raid6_init(), including
+ * the generic ones using raid6_algo_add_default() if wanted.
+ */
+ arch_raid6_init();
+
+ /*
+ * Architectures don't have to set a recovery algorithm, we'll just pick
+ * the generic integer one if none was set.
+ */
+ if (!raid6_recov_algo)
+ raid6_recov_algo = &raid6_recov_intx1;
+ pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name);
+
+#ifdef MODULE
+ return raid6_select_algo();
+#else
+ return 0;
+#endif
}
-static void raid6_exit(void)
+static void __exit raid6_exit(void)
{
- do { } while (0);
}
-subsys_initcall(raid6_select_algo);
+/*
+ * When built-in we must register the default template before md, but we don't
+ * want calibration to run that early as that would delay the boot process.
+ */
+#ifndef MODULE
+__initcall(raid6_select_algo);
+#endif
+core_initcall(raid6_init);
module_exit(raid6_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
+
+#if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST)
+const struct raid6_calls *raid6_algo_find(unsigned int idx)
+{
+ if (idx >= raid6_nr_algos) {
+ /*
+ * Always include the simplest generic integer implementation in
+ * the unit tests as a baseline.
+ */
+ if (idx == raid6_nr_algos &&
+ raid6_algos[0] != &raid6_intx1)
+ return &raid6_intx1;
+ return NULL;
+ }
+ return raid6_algos[idx];
+}
+EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find);
+
+const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx)
+{
+ switch (idx) {
+ case 0:
+ /* always test the generic integer implementation */
+ return &raid6_recov_intx1;
+ case 1:
+ /* test the optimized implementation if there is one */
+ if (raid6_recov_algo != &raid6_recov_intx1)
+ return raid6_recov_algo;
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find);
+#endif /* CONFIG_RAID6_PQ_KUNIT_TEST */
diff --git a/lib/raid/raid6/algos.h b/lib/raid/raid6/algos.h
index e5f1098d2179..43f636be183f 100644
--- a/lib/raid/raid6/algos.h
+++ b/lib/raid/raid6/algos.h
@@ -5,6 +5,7 @@
#ifndef _PQ_IMPL_H
#define _PQ_IMPL_H
+#include <linux/init.h>
#include <linux/raid/pq_tables.h>
/* Routine choices */
@@ -13,70 +14,28 @@ struct raid6_calls {
void (*gen_syndrome)(int disks, size_t bytes, void **ptrs);
void (*xor_syndrome)(int disks, int start, int stop, size_t bytes,
void **ptrs);
- int (*valid)(void); /* Returns 1 if this routine set is usable */
- int priority; /* Relative priority ranking if non-zero */
};
-/* Various routine sets */
-extern const struct raid6_calls raid6_intx1;
-extern const struct raid6_calls raid6_intx2;
-extern const struct raid6_calls raid6_intx4;
-extern const struct raid6_calls raid6_intx8;
-extern const struct raid6_calls raid6_mmxx1;
-extern const struct raid6_calls raid6_mmxx2;
-extern const struct raid6_calls raid6_sse1x1;
-extern const struct raid6_calls raid6_sse1x2;
-extern const struct raid6_calls raid6_sse2x1;
-extern const struct raid6_calls raid6_sse2x2;
-extern const struct raid6_calls raid6_sse2x4;
-extern const struct raid6_calls raid6_altivec1;
-extern const struct raid6_calls raid6_altivec2;
-extern const struct raid6_calls raid6_altivec4;
-extern const struct raid6_calls raid6_altivec8;
-extern const struct raid6_calls raid6_avx2x1;
-extern const struct raid6_calls raid6_avx2x2;
-extern const struct raid6_calls raid6_avx2x4;
-extern const struct raid6_calls raid6_avx512x1;
-extern const struct raid6_calls raid6_avx512x2;
-extern const struct raid6_calls raid6_avx512x4;
-extern const struct raid6_calls raid6_s390vx8;
-extern const struct raid6_calls raid6_vpermxor1;
-extern const struct raid6_calls raid6_vpermxor2;
-extern const struct raid6_calls raid6_vpermxor4;
-extern const struct raid6_calls raid6_vpermxor8;
-extern const struct raid6_calls raid6_lsx;
-extern const struct raid6_calls raid6_lasx;
-extern const struct raid6_calls raid6_rvvx1;
-extern const struct raid6_calls raid6_rvvx2;
-extern const struct raid6_calls raid6_rvvx4;
-extern const struct raid6_calls raid6_rvvx8;
-
struct raid6_recov_calls {
const char *name;
void (*data2)(int disks, size_t bytes, int faila, int failb,
void **ptrs);
void (*datap)(int disks, size_t bytes, int faila, void **ptrs);
- int (*valid)(void);
- int priority;
};
-extern const struct raid6_recov_calls raid6_recov_intx1;
-extern const struct raid6_recov_calls raid6_recov_ssse3;
-extern const struct raid6_recov_calls raid6_recov_avx2;
-extern const struct raid6_recov_calls raid6_recov_avx512;
-extern const struct raid6_recov_calls raid6_recov_s390xc;
-extern const struct raid6_recov_calls raid6_recov_neon;
-extern const struct raid6_recov_calls raid6_recov_lsx;
-extern const struct raid6_recov_calls raid6_recov_lasx;
-extern const struct raid6_recov_calls raid6_recov_rvv;
+void __init raid6_algo_add(const struct raid6_calls *algo);
+void __init raid6_algo_add_default(void);
+void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo);
-extern const struct raid6_calls raid6_neonx1;
-extern const struct raid6_calls raid6_neonx2;
-extern const struct raid6_calls raid6_neonx4;
-extern const struct raid6_calls raid6_neonx8;
+/* for the kunit test */
+const struct raid6_calls *raid6_algo_find(unsigned int idx);
+const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx);
-/* Algorithm list */
-extern const struct raid6_calls * const raid6_algos[];
-extern const struct raid6_recov_calls *const raid6_recov_algos[];
+/* generic implementations */
+extern const struct raid6_calls raid6_intx1;
+extern const struct raid6_calls raid6_intx2;
+extern const struct raid6_calls raid6_intx4;
+extern const struct raid6_calls raid6_intx8;
+extern const struct raid6_recov_calls raid6_recov_intx1;
#endif /* _PQ_IMPL_H */
diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c
index bd4ec4c86ee8..341c61af675e 100644
--- a/lib/raid/raid6/arm/neon.c
+++ b/lib/raid/raid6/arm/neon.c
@@ -42,15 +42,9 @@
struct raid6_calls const raid6_neonx ## _n = { \
.gen_syndrome = raid6_neon ## _n ## _gen_syndrome, \
.xor_syndrome = raid6_neon ## _n ## _xor_syndrome, \
- .valid = raid6_have_neon, \
.name = "neonx" #_n, \
}
-static int raid6_have_neon(void)
-{
- return cpu_has_neon();
-}
-
RAID6_NEON_WRAPPER(1);
RAID6_NEON_WRAPPER(2);
RAID6_NEON_WRAPPER(4);
diff --git a/lib/raid/raid6/arm/pq_arch.h b/lib/raid/raid6/arm/pq_arch.h
new file mode 100644
index 000000000000..a1f4cc4961e9
--- /dev/null
+++ b/lib/raid/raid6/arm/pq_arch.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <asm/neon.h>
+
+extern const struct raid6_calls raid6_neonx1;
+extern const struct raid6_calls raid6_neonx2;
+extern const struct raid6_calls raid6_neonx4;
+extern const struct raid6_calls raid6_neonx8;
+extern const struct raid6_recov_calls raid6_recov_neon;
+
+static __always_inline void __init arch_raid6_init(void)
+{
+ if (cpu_has_neon()) {
+ raid6_algo_add(&raid6_neonx1);
+ raid6_algo_add(&raid6_neonx2);
+ raid6_algo_add(&raid6_neonx4);
+ raid6_algo_add(&raid6_neonx8);
+ }
+
+ raid6_algo_add_default();
+
+ if (cpu_has_neon())
+ raid6_recov_algo_add(&raid6_recov_neon);
+}
diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c
index e1d1d19fc9a8..1524050d09b7 100644
--- a/lib/raid/raid6/arm/recov_neon.c
+++ b/lib/raid/raid6/arm/recov_neon.c
@@ -10,11 +10,6 @@
#include "algos.h"
#include "arm/neon.h"
-static int raid6_has_neon(void)
-{
- return cpu_has_neon();
-}
-
static void raid6_2data_recov_neon(int disks, size_t bytes, int faila,
int failb, void **ptrs)
{
@@ -87,7 +82,5 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_neon = {
.data2 = raid6_2data_recov_neon,
.datap = raid6_datap_recov_neon,
- .valid = raid6_has_neon,
.name = "neon",
- .priority = 10,
};
diff --git a/lib/raid/raid6/arm64/pq_arch.h b/lib/raid/raid6/arm64/pq_arch.h
new file mode 100644
index 000000000000..27ff564d7594
--- /dev/null
+++ b/lib/raid/raid6/arm64/pq_arch.h
@@ -0,0 +1 @@
+#include "arm/pq_arch.h"
diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c
index f77d11ce676e..c1eb53fafd27 100644
--- a/lib/raid/raid6/loongarch/loongarch_simd.c
+++ b/lib/raid/raid6/loongarch/loongarch_simd.c
@@ -26,11 +26,6 @@
#ifdef CONFIG_CPU_HAS_LSX
#define NSIZE 16
-static int raid6_has_lsx(void)
-{
- return cpu_has_lsx;
-}
-
static void raid6_lsx_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
@@ -246,7 +241,6 @@ static void raid6_lsx_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_lsx = {
.gen_syndrome = raid6_lsx_gen_syndrome,
.xor_syndrome = raid6_lsx_xor_syndrome,
- .valid = raid6_has_lsx,
.name = "lsx",
};
@@ -256,11 +250,6 @@ const struct raid6_calls raid6_lsx = {
#ifdef CONFIG_CPU_HAS_LASX
#define NSIZE 32
-static int raid6_has_lasx(void)
-{
- return cpu_has_lasx;
-}
-
static void raid6_lasx_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
@@ -414,7 +403,6 @@ static void raid6_lasx_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_lasx = {
.gen_syndrome = raid6_lasx_gen_syndrome,
.xor_syndrome = raid6_lasx_xor_syndrome,
- .valid = raid6_has_lasx,
.name = "lasx",
};
#undef NSIZE
diff --git a/lib/raid/raid6/loongarch/pq_arch.h b/lib/raid/raid6/loongarch/pq_arch.h
new file mode 100644
index 000000000000..cd4ee2df3f37
--- /dev/null
+++ b/lib/raid/raid6/loongarch/pq_arch.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <asm/cpu-features.h>
+
+extern const struct raid6_calls raid6_lsx;
+extern const struct raid6_calls raid6_lasx;
+
+extern const struct raid6_recov_calls raid6_recov_lsx;
+extern const struct raid6_recov_calls raid6_recov_lasx;
+
+static __always_inline void __init arch_raid6_init(void)
+{
+ if (IS_ENABLED(CONFIG_CPU_HAS_LSX) && cpu_has_lsx)
+ raid6_algo_add(&raid6_lsx);
+ if (IS_ENABLED(CONFIG_CPU_HAS_LASX) && cpu_has_lasx)
+ raid6_algo_add(&raid6_lasx);
+ raid6_algo_add_default();
+
+ if (IS_ENABLED(CONFIG_CPU_HAS_LASX) && cpu_has_lasx)
+ raid6_recov_algo_add(&raid6_recov_lasx);
+ else if (IS_ENABLED(CONFIG_CPU_HAS_LSX) && cpu_has_lsx)
+ raid6_recov_algo_add(&raid6_recov_lsx);
+}
diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
index 0bbdc8b5c2e7..87a2313bbb4f 100644
--- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c
+++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
@@ -24,11 +24,6 @@
*/
#ifdef CONFIG_CPU_HAS_LSX
-static int raid6_has_lsx(void)
-{
- return cpu_has_lsx;
-}
-
static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila,
int failb, void **ptrs)
{
@@ -291,18 +286,11 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_lsx = {
.data2 = raid6_2data_recov_lsx,
.datap = raid6_datap_recov_lsx,
- .valid = raid6_has_lsx,
.name = "lsx",
- .priority = 1,
};
#endif /* CONFIG_CPU_HAS_LSX */
#ifdef CONFIG_CPU_HAS_LASX
-static int raid6_has_lasx(void)
-{
- return cpu_has_lasx;
-}
-
static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila,
int failb, void **ptrs)
{
@@ -509,8 +497,6 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_lasx = {
.data2 = raid6_2data_recov_lasx,
.datap = raid6_datap_recov_lasx,
- .valid = raid6_has_lasx,
.name = "lasx",
- .priority = 2,
};
#endif /* CONFIG_CPU_HAS_LASX */
diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc
index eb4a448cc88e..c5429fb71dd6 100644
--- a/lib/raid/raid6/powerpc/altivec.uc
+++ b/lib/raid/raid6/powerpc/altivec.uc
@@ -104,17 +104,7 @@ static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
preempt_enable();
}
-int raid6_have_altivec(void);
-#if $# == 1
-int raid6_have_altivec(void)
-{
- /* This assumes either all CPUs have Altivec or none does */
- return cpu_has_feature(CPU_FTR_ALTIVEC);
-}
-#endif
-
const struct raid6_calls raid6_altivec$# = {
.gen_syndrome = raid6_altivec$#_gen_syndrome,
- .valid = raid6_have_altivec,
.name = "altivecx$#",
};
diff --git a/lib/raid/raid6/powerpc/pq_arch.h b/lib/raid/raid6/powerpc/pq_arch.h
new file mode 100644
index 000000000000..ecae7d3be131
--- /dev/null
+++ b/lib/raid/raid6/powerpc/pq_arch.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <asm/cputable.h>
+
+extern const struct raid6_calls raid6_altivec1;
+extern const struct raid6_calls raid6_altivec2;
+extern const struct raid6_calls raid6_altivec4;
+extern const struct raid6_calls raid6_altivec8;
+extern const struct raid6_calls raid6_vpermxor1;
+extern const struct raid6_calls raid6_vpermxor2;
+extern const struct raid6_calls raid6_vpermxor4;
+extern const struct raid6_calls raid6_vpermxor8;
+
+static __always_inline void __init arch_raid6_init(void)
+{
+ /* This assumes either all CPUs have Altivec or none does */
+ if (cpu_has_feature(CPU_FTR_ALTIVEC)) {
+ raid6_algo_add(&raid6_altivec1);
+ raid6_algo_add(&raid6_altivec2);
+ raid6_algo_add(&raid6_altivec4);
+ raid6_algo_add(&raid6_altivec8);
+ }
+ if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) &&
+ cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ raid6_algo_add(&raid6_vpermxor1);
+ raid6_algo_add(&raid6_vpermxor2);
+ raid6_algo_add(&raid6_vpermxor4);
+ raid6_algo_add(&raid6_vpermxor8);
+ }
+ raid6_algo_add_default();
+}
diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc
index ec61f30bec11..e8964361aaef 100644
--- a/lib/raid/raid6/powerpc/vpermxor.uc
+++ b/lib/raid/raid6/powerpc/vpermxor.uc
@@ -76,18 +76,7 @@ static void raid6_vpermxor$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
preempt_enable();
}
-int raid6_have_altivec_vpermxor(void);
-#if $# == 1
-int raid6_have_altivec_vpermxor(void)
-{
- /* Check if arch has both altivec and the vpermxor instructions */
- return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) &&
- cpu_has_feature(CPU_FTR_ARCH_207S));
-}
-#endif
-
const struct raid6_calls raid6_vpermxor$# = {
.gen_syndrome = raid6_vpermxor$#_gen_syndrome,
- .valid = raid6_have_altivec_vpermxor,
.name = "vpermxor$#",
};
diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c
index 735ab4013771..76eb2aef3667 100644
--- a/lib/raid/raid6/recov.c
+++ b/lib/raid/raid6/recov.c
@@ -97,7 +97,5 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_intx1 = {
.data2 = raid6_2data_recov_intx1,
.datap = raid6_datap_recov_intx1,
- .valid = NULL,
.name = "intx1",
- .priority = 0,
};
diff --git a/lib/raid/raid6/riscv/pq_arch.h b/lib/raid/raid6/riscv/pq_arch.h
new file mode 100644
index 000000000000..52dd01e9fc42
--- /dev/null
+++ b/lib/raid/raid6/riscv/pq_arch.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <asm/vector.h>
+
+extern const struct raid6_calls raid6_rvvx1;
+extern const struct raid6_calls raid6_rvvx2;
+extern const struct raid6_calls raid6_rvvx4;
+extern const struct raid6_calls raid6_rvvx8;
+extern const struct raid6_recov_calls raid6_recov_rvv;
+
+static __always_inline void __init arch_raid6_init(void)
+{
+ if (has_vector()) {
+ raid6_algo_add(&raid6_rvvx1);
+ raid6_algo_add(&raid6_rvvx2);
+ raid6_algo_add(&raid6_rvvx4);
+ raid6_algo_add(&raid6_rvvx8);
+ raid6_recov_algo_add(&raid6_recov_rvv);
+ }
+ raid6_algo_add_default();
+}
diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c
index 02120d245e22..2305940276dd 100644
--- a/lib/raid/raid6/riscv/recov_rvv.c
+++ b/lib/raid/raid6/riscv/recov_rvv.c
@@ -218,7 +218,5 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_rvv = {
.data2 = raid6_2data_recov_rvv,
.datap = raid6_datap_recov_rvv,
- .valid = rvv_has_vector,
.name = "rvv",
- .priority = 1,
};
diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h
index c293130d798b..3a7c2468b1ea 100644
--- a/lib/raid/raid6/riscv/rvv.h
+++ b/lib/raid/raid6/riscv/rvv.h
@@ -10,11 +10,6 @@
#include <asm/vector.h>
#include "algos.h"
-static int rvv_has_vector(void)
-{
- return has_vector();
-}
-
#define RAID6_RVV_WRAPPER(_n) \
static void raid6_rvv ## _n ## _gen_syndrome(int disks, \
size_t bytes, void **ptrs) \
@@ -41,6 +36,5 @@ static int rvv_has_vector(void)
struct raid6_calls const raid6_rvvx ## _n = { \
.gen_syndrome = raid6_rvv ## _n ## _gen_syndrome, \
.xor_syndrome = raid6_rvv ## _n ## _xor_syndrome, \
- .valid = rvv_has_vector, \
.name = "rvvx" #_n, \
}
diff --git a/lib/raid/raid6/s390/pq_arch.h b/lib/raid/raid6/s390/pq_arch.h
new file mode 100644
index 000000000000..95d14c342306
--- /dev/null
+++ b/lib/raid/raid6/s390/pq_arch.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <linux/cpufeature.h>
+
+extern const struct raid6_calls raid6_s390vx8;
+extern const struct raid6_recov_calls raid6_recov_s390xc;
+
+static __always_inline void __init arch_raid6_init(void)
+{
+ if (cpu_has_vx())
+ raid6_algo_add(&raid6_s390vx8);
+ else
+ raid6_algo_add_default();
+ raid6_recov_algo_add(&raid6_recov_s390xc);
+}
diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c
index e7b3409f21e2..08d56896e5ea 100644
--- a/lib/raid/raid6/s390/recov_s390xc.c
+++ b/lib/raid/raid6/s390/recov_s390xc.c
@@ -112,7 +112,5 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_s390xc = {
.data2 = raid6_2data_recov_s390xc,
.datap = raid6_datap_recov_s390xc,
- .valid = NULL,
.name = "s390xc",
- .priority = 1,
};
diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc
index aba3515eacac..e5cf9054be2a 100644
--- a/lib/raid/raid6/s390/s390vx.uc
+++ b/lib/raid/raid6/s390/s390vx.uc
@@ -121,15 +121,8 @@ static void raid6_s390vx$#_xor_syndrome(int disks, int start, int stop,
kernel_fpu_end(&vxstate, KERNEL_VXR);
}
-static int raid6_s390vx$#_valid(void)
-{
- return cpu_has_vx();
-}
-
const struct raid6_calls raid6_s390vx$# = {
.gen_syndrome = raid6_s390vx$#_gen_syndrome,
.xor_syndrome = raid6_s390vx$#_xor_syndrome,
- .valid = raid6_s390vx$#_valid,
.name = "vx128x$#",
- .priority = 1,
};
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 9b71f22fa19a..daaa28e96ff1 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -88,19 +88,20 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls,
static void raid6_test(struct kunit *test)
{
- const struct raid6_calls *const *algo;
- const struct raid6_recov_calls *const *ra;
int i, j, p1, p2;
+ unsigned int r, g;
- for (ra = raid6_recov_algos; *ra; ra++) {
- if ((*ra)->valid && !(*ra)->valid())
- continue;
+ for (r = 0; ; r++) {
+ const struct raid6_recov_calls *ra = raid6_recov_algo_find(r);
- for (algo = raid6_algos; *algo; algo++) {
- const struct raid6_calls *calls = *algo;
+ if (!ra)
+ break;
- if (calls->valid && !calls->valid())
- continue;
+ for (g = 0; ; g++) {
+ const struct raid6_calls *calls = raid6_algo_find(g);
+
+ if (!calls)
+ break;
/* Nuke syndromes */
memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
@@ -112,7 +113,7 @@ static void raid6_test(struct kunit *test)
for (i = 0; i < NDISKS-1; i++)
for (j = i+1; j < NDISKS; j++)
- test_disks(test, calls, *ra, i, j);
+ test_disks(test, calls, ra, i, j);
if (!calls->xor_syndrome)
continue;
@@ -130,7 +131,7 @@ static void raid6_test(struct kunit *test)
for (i = 0; i < NDISKS-1; i++)
for (j = i+1; j < NDISKS; j++)
test_disks(test, calls,
- *ra, i, j);
+ ra, i, j);
}
}
diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c
index 0bf831799082..7efd94e6a87a 100644
--- a/lib/raid/raid6/x86/avx2.c
+++ b/lib/raid/raid6/x86/avx2.c
@@ -24,11 +24,6 @@ static const struct raid6_avx2_constants {
0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL,},
};
-static int raid6_have_avx2(void)
-{
- return boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX);
-}
-
/*
* Plain AVX2 implementation
*/
@@ -131,10 +126,7 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_avx2x1 = {
.gen_syndrome = raid6_avx21_gen_syndrome,
.xor_syndrome = raid6_avx21_xor_syndrome,
- .valid = raid6_have_avx2,
.name = "avx2x1",
- /* Prefer AVX2 over priority 1 (SSE2 and others) */
- .priority = 2,
};
/*
@@ -262,10 +254,7 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_avx2x2 = {
.gen_syndrome = raid6_avx22_gen_syndrome,
.xor_syndrome = raid6_avx22_xor_syndrome,
- .valid = raid6_have_avx2,
.name = "avx2x2",
- /* Prefer AVX2 over priority 1 (SSE2 and others) */
- .priority = 2,
};
#ifdef CONFIG_X86_64
@@ -466,9 +455,6 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_avx2x4 = {
.gen_syndrome = raid6_avx24_gen_syndrome,
.xor_syndrome = raid6_avx24_xor_syndrome,
- .valid = raid6_have_avx2,
.name = "avx2x4",
- /* Prefer AVX2 over priority 1 (SSE2 and others) */
- .priority = 2,
};
#endif /* CONFIG_X86_64 */
diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c
index 98ed42fb0a46..0772e798b742 100644
--- a/lib/raid/raid6/x86/avx512.c
+++ b/lib/raid/raid6/x86/avx512.c
@@ -30,16 +30,6 @@ static const struct raid6_avx512_constants {
0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL,},
};
-static int raid6_have_avx512(void)
-{
- return boot_cpu_has(X86_FEATURE_AVX2) &&
- boot_cpu_has(X86_FEATURE_AVX) &&
- boot_cpu_has(X86_FEATURE_AVX512F) &&
- boot_cpu_has(X86_FEATURE_AVX512BW) &&
- boot_cpu_has(X86_FEATURE_AVX512VL) &&
- boot_cpu_has(X86_FEATURE_AVX512DQ);
-}
-
static void raid6_avx5121_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
@@ -159,10 +149,7 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_avx512x1 = {
.gen_syndrome = raid6_avx5121_gen_syndrome,
.xor_syndrome = raid6_avx5121_xor_syndrome,
- .valid = raid6_have_avx512,
.name = "avx512x1",
- /* Prefer AVX512 over priority 1 (SSE2 and others) */
- .priority = 2,
};
/*
@@ -317,10 +304,7 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_avx512x2 = {
.gen_syndrome = raid6_avx5122_gen_syndrome,
.xor_syndrome = raid6_avx5122_xor_syndrome,
- .valid = raid6_have_avx512,
.name = "avx512x2",
- /* Prefer AVX512 over priority 1 (SSE2 and others) */
- .priority = 2,
};
#ifdef CONFIG_X86_64
@@ -556,9 +540,6 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_avx512x4 = {
.gen_syndrome = raid6_avx5124_gen_syndrome,
.xor_syndrome = raid6_avx5124_xor_syndrome,
- .valid = raid6_have_avx512,
.name = "avx512x4",
- /* Prefer AVX512 over priority 1 (SSE2 and others) */
- .priority = 2,
};
#endif
diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c
index 052d9f010bfe..3228c335965a 100644
--- a/lib/raid/raid6/x86/mmx.c
+++ b/lib/raid/raid6/x86/mmx.c
@@ -22,12 +22,6 @@ const struct raid6_mmx_constants {
0x1d1d1d1d1d1d1d1dULL,
};
-static int raid6_have_mmx(void)
-{
- /* Not really "boot_cpu" but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX);
-}
-
/*
* Plain MMX implementation
*/
@@ -70,7 +64,6 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs)
const struct raid6_calls raid6_mmxx1 = {
.gen_syndrome = raid6_mmx1_gen_syndrome,
- .valid = raid6_have_mmx,
.name = "mmxx1",
};
@@ -127,6 +120,5 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs)
const struct raid6_calls raid6_mmxx2 = {
.gen_syndrome = raid6_mmx2_gen_syndrome,
- .valid = raid6_have_mmx,
.name = "mmxx2",
};
diff --git a/lib/raid/raid6/x86/pq_arch.h b/lib/raid/raid6/x86/pq_arch.h
new file mode 100644
index 000000000000..0493fc3a089c
--- /dev/null
+++ b/lib/raid/raid6/x86/pq_arch.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <asm/cpufeature.h>
+
+extern const struct raid6_calls raid6_mmxx1;
+extern const struct raid6_calls raid6_mmxx2;
+extern const struct raid6_calls raid6_sse1x1;
+extern const struct raid6_calls raid6_sse1x2;
+extern const struct raid6_calls raid6_sse2x1;
+extern const struct raid6_calls raid6_sse2x2;
+extern const struct raid6_calls raid6_sse2x4;
+extern const struct raid6_calls raid6_avx2x1;
+extern const struct raid6_calls raid6_avx2x2;
+extern const struct raid6_calls raid6_avx2x4;
+extern const struct raid6_calls raid6_avx512x1;
+extern const struct raid6_calls raid6_avx512x2;
+extern const struct raid6_calls raid6_avx512x4;
+
+extern const struct raid6_recov_calls raid6_recov_ssse3;
+extern const struct raid6_recov_calls raid6_recov_avx2;
+extern const struct raid6_recov_calls raid6_recov_avx512;
+
+static inline int raid6_has_avx512(void)
+{
+ return boot_cpu_has(X86_FEATURE_AVX2) &&
+ boot_cpu_has(X86_FEATURE_AVX) &&
+ boot_cpu_has(X86_FEATURE_AVX512F) &&
+ boot_cpu_has(X86_FEATURE_AVX512BW) &&
+ boot_cpu_has(X86_FEATURE_AVX512VL) &&
+ boot_cpu_has(X86_FEATURE_AVX512DQ);
+}
+
+static inline bool raid6_has_avx2(void)
+{
+ return boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX);
+}
+
+static inline bool raid6_has_ssse3(void)
+{
+ return boot_cpu_has(X86_FEATURE_XMM) &&
+ boot_cpu_has(X86_FEATURE_XMM2) &&
+ boot_cpu_has(X86_FEATURE_SSSE3);
+}
+
+static inline bool raid6_has_sse2(void)
+{
+ return boot_cpu_has(X86_FEATURE_MMX) &&
+ boot_cpu_has(X86_FEATURE_FXSR) &&
+ boot_cpu_has(X86_FEATURE_XMM) &&
+ boot_cpu_has(X86_FEATURE_XMM2);
+}
+
+static inline bool raid6_has_sse1_or_mmxext(void)
+{
+ return boot_cpu_has(X86_FEATURE_MMX) &&
+ (boot_cpu_has(X86_FEATURE_XMM) ||
+ boot_cpu_has(X86_FEATURE_MMXEXT));
+}
+
+static __always_inline void __init arch_raid6_init(void)
+{
+ if (raid6_has_avx2()) {
+ if (raid6_has_avx512()) {
+ raid6_algo_add(&raid6_avx512x1);
+ raid6_algo_add(&raid6_avx512x2);
+ if (IS_ENABLED(CONFIG_X86_64))
+ raid6_algo_add(&raid6_avx512x4);
+ }
+ raid6_algo_add(&raid6_avx2x1);
+ raid6_algo_add(&raid6_avx2x2);
+ if (IS_ENABLED(CONFIG_X86_64))
+ raid6_algo_add(&raid6_avx2x4);
+ } else if (IS_ENABLED(CONFIG_X86_64) || raid6_has_sse2()) {
+ /* x86_64 can assume SSE2 as baseline */
+ raid6_algo_add(&raid6_sse2x1);
+ raid6_algo_add(&raid6_sse2x2);
+ if (IS_ENABLED(CONFIG_X86_64))
+ raid6_algo_add(&raid6_sse2x4);
+ } else if (raid6_has_sse1_or_mmxext()) {
+ raid6_algo_add(&raid6_sse1x1);
+ raid6_algo_add(&raid6_sse1x2);
+ raid6_algo_add_default();
+ } else {
+ if (boot_cpu_has(X86_FEATURE_MMX)) {
+ raid6_algo_add(&raid6_mmxx1);
+ raid6_algo_add(&raid6_mmxx2);
+ }
+ raid6_algo_add_default();
+ }
+
+ if (raid6_has_avx512())
+ raid6_recov_algo_add(&raid6_recov_avx512);
+ else if (raid6_has_avx2())
+ raid6_recov_algo_add(&raid6_recov_avx2);
+ else if (raid6_has_ssse3())
+ raid6_recov_algo_add(&raid6_recov_ssse3);
+}
diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c
index 06c6e05763bc..a714a780a2d8 100644
--- a/lib/raid/raid6/x86/recov_avx2.c
+++ b/lib/raid/raid6/x86/recov_avx2.c
@@ -9,12 +9,6 @@
#include <asm/fpu/api.h>
#include "algos.h"
-static int raid6_has_avx2(void)
-{
- return boot_cpu_has(X86_FEATURE_AVX2) &&
- boot_cpu_has(X86_FEATURE_AVX);
-}
-
static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila,
int failb, void **ptrs)
{
@@ -305,11 +299,9 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_avx2 = {
.data2 = raid6_2data_recov_avx2,
.datap = raid6_datap_recov_avx2,
- .valid = raid6_has_avx2,
#ifdef CONFIG_X86_64
.name = "avx2x2",
#else
.name = "avx2x1",
#endif
- .priority = 2,
};
diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c
index 850bb962b514..ec72d5a30c01 100644
--- a/lib/raid/raid6/x86/recov_avx512.c
+++ b/lib/raid/raid6/x86/recov_avx512.c
@@ -11,16 +11,6 @@
#include <asm/fpu/api.h>
#include "algos.h"
-static int raid6_has_avx512(void)
-{
- return boot_cpu_has(X86_FEATURE_AVX2) &&
- boot_cpu_has(X86_FEATURE_AVX) &&
- boot_cpu_has(X86_FEATURE_AVX512F) &&
- boot_cpu_has(X86_FEATURE_AVX512BW) &&
- boot_cpu_has(X86_FEATURE_AVX512VL) &&
- boot_cpu_has(X86_FEATURE_AVX512DQ);
-}
-
static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila,
int failb, void **ptrs)
{
@@ -369,11 +359,9 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_avx512 = {
.data2 = raid6_2data_recov_avx512,
.datap = raid6_datap_recov_avx512,
- .valid = raid6_has_avx512,
#ifdef CONFIG_X86_64
.name = "avx512x2",
#else
.name = "avx512x1",
#endif
- .priority = 3,
};
diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c
index 95589c33003a..700bd2c865ec 100644
--- a/lib/raid/raid6/x86/recov_ssse3.c
+++ b/lib/raid/raid6/x86/recov_ssse3.c
@@ -8,13 +8,6 @@
#include <asm/fpu/api.h>
#include "algos.h"
-static int raid6_has_ssse3(void)
-{
- return boot_cpu_has(X86_FEATURE_XMM) &&
- boot_cpu_has(X86_FEATURE_XMM2) &&
- boot_cpu_has(X86_FEATURE_SSSE3);
-}
-
static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila,
int failb, void **ptrs)
{
@@ -320,11 +313,9 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila,
const struct raid6_recov_calls raid6_recov_ssse3 = {
.data2 = raid6_2data_recov_ssse3,
.datap = raid6_datap_recov_ssse3,
- .valid = raid6_has_ssse3,
#ifdef CONFIG_X86_64
.name = "ssse3x2",
#else
.name = "ssse3x1",
#endif
- .priority = 1,
};
diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c
index 7004255a0bb1..6ebdcf824e00 100644
--- a/lib/raid/raid6/x86/sse1.c
+++ b/lib/raid/raid6/x86/sse1.c
@@ -25,14 +25,6 @@ extern const struct raid6_mmx_constants {
u64 x1d;
} raid6_mmx_constants;
-static int raid6_have_sse1_or_mmxext(void)
-{
- /* Not really boot_cpu but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX) &&
- (boot_cpu_has(X86_FEATURE_XMM) ||
- boot_cpu_has(X86_FEATURE_MMXEXT));
-}
-
/*
* Plain SSE1 implementation
*/
@@ -86,9 +78,7 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs)
const struct raid6_calls raid6_sse1x1 = {
.gen_syndrome = raid6_sse11_gen_syndrome,
- .valid = raid6_have_sse1_or_mmxext,
.name = "sse1x1",
- .priority = 1, /* Has cache hints */
};
/*
@@ -148,7 +138,5 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs)
const struct raid6_calls raid6_sse1x2 = {
.gen_syndrome = raid6_sse12_gen_syndrome,
- .valid = raid6_have_sse1_or_mmxext,
.name = "sse1x2",
- .priority = 1, /* Has cache hints */
};
diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c
index f30be4ee14d0..7049c8512f35 100644
--- a/lib/raid/raid6/x86/sse2.c
+++ b/lib/raid/raid6/x86/sse2.c
@@ -22,15 +22,6 @@ static const struct raid6_sse_constants {
{ 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL },
};
-static int raid6_have_sse2(void)
-{
- /* Not really boot_cpu but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX) &&
- boot_cpu_has(X86_FEATURE_FXSR) &&
- boot_cpu_has(X86_FEATURE_XMM) &&
- boot_cpu_has(X86_FEATURE_XMM2);
-}
-
/*
* Plain SSE2 implementation
*/
@@ -136,9 +127,7 @@ static void raid6_sse21_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_sse2x1 = {
.gen_syndrome = raid6_sse21_gen_syndrome,
.xor_syndrome = raid6_sse21_xor_syndrome,
- .valid = raid6_have_sse2,
.name = "sse2x1",
- .priority = 1, /* Has cache hints */
};
/*
@@ -266,9 +255,7 @@ static void raid6_sse22_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_sse2x2 = {
.gen_syndrome = raid6_sse22_gen_syndrome,
.xor_syndrome = raid6_sse22_xor_syndrome,
- .valid = raid6_have_sse2,
.name = "sse2x2",
- .priority = 1, /* Has cache hints */
};
#ifdef CONFIG_X86_64
@@ -473,9 +460,7 @@ static void raid6_sse24_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_sse2x4 = {
.gen_syndrome = raid6_sse24_gen_syndrome,
.xor_syndrome = raid6_sse24_xor_syndrome,
- .valid = raid6_have_sse2,
.name = "sse2x4",
- .priority = 1, /* Has cache hints */
};
#endif /* CONFIG_X86_64 */
--
2.53.0
^ permalink raw reply related
* [PATCH 19/19] raid6_kunit: randomize buffer alignment
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
Add code to add random alignment to the buffers to test the case where
they are not page aligned, and to move the buffers to the end of the
allocation so that they are next to the vmalloc guard page.
This does not include the recovery buffers as the recovery requires
page alignment.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 41 +++++++++++++++++++++++++-----
1 file changed, 35 insertions(+), 6 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index d6ac777dcaee..7b45c7be36fc 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -21,6 +21,7 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct rnd_state rng;
static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
+static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
static size_t test_buflen;
@@ -50,6 +51,14 @@ static unsigned int random_nr_buffers(void)
RAID6_MIN_DISKS;
}
+/* Generate a random alignment that is a multiple of 64. */
+static unsigned int random_alignment(unsigned int max_alignment)
+{
+ if (max_alignment == 0)
+ return 0;
+ return (rand32() % (max_alignment + 1)) & ~63;
+}
+
static void makedata(int start, int stop)
{
int i;
@@ -80,7 +89,7 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
memset(test_recov_buffers[i], 0xf0, test_buflen);
- memcpy(dataptrs, test_buffers, sizeof(dataptrs));
+ memcpy(dataptrs, aligned_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
@@ -102,13 +111,13 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
}
- KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
+ KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila],
len,
"faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
faila, member_type(nr_buffers, faila),
nr_buffers, len,
failb, member_type(nr_buffers, failb));
- KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
+ KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb],
len,
"failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
failb, member_type(nr_buffers, failb),
@@ -152,9 +161,9 @@ static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
{
const struct test_args *ta = test->param_value;
- ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
makedata(p1, p2);
- ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
test_recover(test, nr_buffers, len);
}
@@ -178,13 +187,33 @@ static void raid6_test_one(struct kunit *test)
const struct test_args *ta = test->param_value;
unsigned int nr_buffers = random_nr_buffers();
unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
+ unsigned int max_alignment;
+ int i;
/* Nuke syndromes */
memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
+ /*
+ * If we're not using the entire buffer size, inject randomize alignment
+ * into the buffer.
+ */
+ max_alignment = RAID6_KUNIT_MAX_BYTES - len;
+ if (rand32() % 2 == 0) {
+ /* Use random alignments mod 64 */
+ for (i = 0; i < nr_buffers; i++)
+ aligned_buffers[i] = test_buffers[i] +
+ random_alignment(max_alignment);
+ } else {
+ /* Go up to the guard page, to catch buffer overreads */
+ unsigned int align = test_buflen - len;
+
+ for (i = 0; i < nr_buffers; i++)
+ aligned_buffers[i] = test_buffers[i] + align;
+ }
+
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(nr_buffers, len, test_buffers);
+ ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers);
test_recover(test, nr_buffers, len);
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox