* FYI: i.MX8MP ISP (RKISP1) MI registers corruption
@ 2025-07-17 9:00 Krzysztof Hałasa
2025-07-17 13:03 ` Krzysztof Hałasa
0 siblings, 1 reply; 23+ messages in thread
From: Krzysztof Hałasa @ 2025-07-17 9:00 UTC (permalink / raw)
To: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner
Cc: Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media,
linux-rockchip, linux-arm-kernel, linux-kernel
Hi,
there is a well-known issue in i.MX8MP ISP (Image Signal Processor)
implementation, resulting in a corruption on accesses to ISP MI (memory
interface) registers.
The "reference" (NXP) VVCam driver simply does the operations twice
(i.e., reads twice with the first result discarded, and writes twice).
This fixes the problem on most accesses, but the problems still persist.
It appears that it occurs only after certain system reboots. E.g. after
a boot the problems are present consistently, I can stop and restart the
camera application and they are still there, until the next boot. On the
next boot they can be gone for good. The problems show themselves maybe
in 5% or 10% of boots.
########### READ operations ###########
I have performed some tests and it appears that, at least in read case,
the corruption is caused by flipping bits in register address.
For example: I'm reading (in userspace, 32-bit transfers) 0x32E21478
(mi_mp_y_base_ad_shd) a million times. I get the following results (the
first 5 are valid):
value 0x3C300000 count 184163
value 0x3C3C0000 count 220357
value 0x3C0C0000 count 203534
value 0x3C180000 count 202677
value 0x3C240000 count 189240
value 0 count 23
value 0x1E2200E0 count 1 <<<< looks like register 32E21578
value 0x1E0A00E0 count 1 <<<< looks like register 32E21578
value 0x1E1000E0 count 1 <<<< looks like register 32E21578
value 0x1E1600E0 count 2 <<<< looks like register 32E21578
value 0x12B95456 count 1 <<<< looks like register 32E2153C
Apparently, the data can come from any MI register.
A possible partial workaround involves doing a "ldp wzr, %w0, [%x1]"
operation (this is good for xxx4 and xxxC registers). The first 32 bits
(from xxx0 or xxx8) are corrupted and ignored (in wzr register), but the
real data (xxx4 or xxxC) going to %w0 is ok.
Another one is "ldp xzr, %x0, [%x1]" and is needed for xxx8 registers.
The first transfer (xxx0 and xxx4) if corrupted, then xxx8 and xxxC is
valid (in %x0).
Unfortunately, the above doesn't work for xxx0, they can be corrupted
anyway. Tried 128-bit %v0 transfers, too. ATM I'm doing multiple reads
and pick the most frequent value.
Current values of the ISP MI registers for reference (all-zero rows not
shown):
xx0 xx4 xx8 xxC
-----------------------------------
32E21400: 7A2001 20 3C180000 1FA400
32E21410: 0 0 0 3C200000
32E21420: FD200 0 0 0
32E21430: 0 0 0 B105488E
32E21470: 0 10001 3C0C0000 1FA400
32E21480: 0 0 3C140000 FD200
32E214F0: 0 0 1 3C
32E21500: 0 0 0 2 <<< corrupted write?
32E21530: 0 0 0 12B95456 <<< corrupted write
32E21540: 0 0 0 2000000
32E21550: 780 0 0 332
32E21560: 0 780 438 1FA400
32E21570: 0 1E1E00EF 1E2200E0 0
########### WRITE operations ###########
My current strategy is doing verify after write, then rinse and repeat
until success. This appears to have a side effect, though: the data can
go to a register different that requested. This manifests itself visibly
in registers which are usually 0 (like the 0x32E2153C above). Not very
frequent, though (but it's dangerous: certain registers contain RAM
addresses for video buffers and I guess ISP can corrupt large areas of
memory this way).
I wonder why it only occurs only after certain reboots.
It also appears (will have to verify) than on another identical(?)
device these problems don't show up at all. I'm still investigating.
The camera is a custom board using a Compulab UCM IMX8M PLUS module.
I guess I could try replicating this issue on a Hummingboard i.MX8MP
board.
I wonder if Rockchip ISP blocks (using a very similar ISP, which in both
cases comes from VeriSilicon) are free of these issues.
Any ideas maybe?
--
Krzysztof "Chris" Hałasa
Sieć Badawcza Łukasiewicz
Przemysłowy Instytut Automatyki i Pomiarów PIAP
Al. Jerozolimskie 202, 02-486 Warszawa
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-17 9:00 FYI: i.MX8MP ISP (RKISP1) MI registers corruption Krzysztof Hałasa @ 2025-07-17 13:03 ` Krzysztof Hałasa 2025-07-21 8:46 ` Stefan Klug 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-07-17 13:03 UTC (permalink / raw) To: Dafna Hirschfeld Cc: Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel > The "reference" (NXP) VVCam driver simply does the operations twice > (i.e., reads twice with the first result discarded, and writes twice). > This fixes the problem on most accesses, but the problems still persist. It appears the corruptions are quite frequent, though. Using "ldp qXX, qYY, [x0]" (2 * 128-bit load pair) I get results like this (each data row is a result of a single ldp): addr: 32E21400 32E21404 32E21408 32E2140C 32E21410 32E21414 32E21418 32E2141C -------------------------------------------------------------------------------- values 3D000007 20 3C300000 1FA400 0 0 0 3C380000 count 99993097 values 0 20 3C300000 1FA400 0 0 0 3C380000 count 5930 values 7E900 20 3C300000 1FA400 0 0 0 3C380000 count 338 values FD200 20 3C300000 1FA400 0 0 0 3C380000 count 223 values 3C380000 20 3C300000 1FA400 0 0 0 3C380000 count 220 values 40 20 3C300000 1FA400 0 0 0 3C380000 count 192 The valid value (in 0x32E21400 register) is 3D000007 only, the rest are corruptions: ca. 6 errors per 100k reads. With other registers, 15 errors per 100k reads etc. I also got this: addr: 32E213F0 32E213F4 32E213F8 32E213FC 32E21400 32E21404 32E21408 32E2140C -------------------------------------------------------------------------------- values 0 0 0 0 3D000007 20 3C300000 1FA400 count 98638773 values 0 0 0 0 0 20 3C300000 1FA400 count 1330227 values 0 0 0 0 40 20 3C300000 1FA400 count 3721 values 0 0 0 0 3C380000 20 3C300000 1FA400 count 314 values 0 0 0 0 7E900 20 3C300000 1FA400 count 572 values 0 0 0 0 FD200 20 3C300000 1FA400 count 428 values 0 0 0 0 4C010 20 3C300000 1FA400 count 25965 which is ca. 14 errors per 1k reads, though maybe it's special - non-MI/MI boundary (at 0x32E21400), reserved addresses (0x32E213Fx) etc. > The problems show themselves maybe in 5% or 10% of boots. Well, now it appears more like 20%: e.g. in 41 system runs (soft reboots only, no power-downs), I got problems 8 times. Obviously I can post the tester source if anyone is interested. Generally ISP MI register read accesses which can be corrupted are: - first 32 bits read in a given transfer, and additionally - every 32 bits on a 32-byte boundary (addresses 0x....00, ...20 etc.). This means, in practice, on i.MX8MP only, RKISP1_CIF_MI_CTRL and RKISP1_CIF_MI_MP_CB_SIZE_INIT (with the workaround). What is this 32-byte boundary? Writing is a bigger problem, though. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-17 13:03 ` Krzysztof Hałasa @ 2025-07-21 8:46 ` Stefan Klug 2025-07-21 13:16 ` Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Stefan Klug @ 2025-07-21 8:46 UTC (permalink / raw) To: Dafna Hirschfeld, Krzysztof Hałasa Cc: Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Krzysztof, Thanks for your investigations. This is all quite worrisome to say the least. Quoting Krzysztof Hałasa (2025-07-17 15:03:54) > > The "reference" (NXP) VVCam driver simply does the operations twice > > (i.e., reads twice with the first result discarded, and writes twice). > > This fixes the problem on most accesses, but the problems still persist. > > It appears the corruptions are quite frequent, though. > Using "ldp qXX, qYY, [x0]" (2 * 128-bit load pair) I get results like > this (each data row is a result of a single ldp): > > addr: 32E21400 32E21404 32E21408 32E2140C 32E21410 32E21414 32E21418 32E2141C > -------------------------------------------------------------------------------- > values 3D000007 20 3C300000 1FA400 0 0 0 3C380000 count 99993097 > values 0 20 3C300000 1FA400 0 0 0 3C380000 count 5930 > values 7E900 20 3C300000 1FA400 0 0 0 3C380000 count 338 > values FD200 20 3C300000 1FA400 0 0 0 3C380000 count 223 > values 3C380000 20 3C300000 1FA400 0 0 0 3C380000 count 220 > values 40 20 3C300000 1FA400 0 0 0 3C380000 count 192 > > The valid value (in 0x32E21400 register) is 3D000007 only, the rest are > corruptions: ca. 6 errors per 100k reads. With other registers, 15 errors > per 100k reads etc. > > I also got this: > addr: 32E213F0 32E213F4 32E213F8 32E213FC 32E21400 32E21404 32E21408 32E2140C > -------------------------------------------------------------------------------- > values 0 0 0 0 3D000007 20 3C300000 1FA400 count 98638773 > values 0 0 0 0 0 20 3C300000 1FA400 count 1330227 > values 0 0 0 0 40 20 3C300000 1FA400 count 3721 > values 0 0 0 0 3C380000 20 3C300000 1FA400 count 314 > values 0 0 0 0 7E900 20 3C300000 1FA400 count 572 > values 0 0 0 0 FD200 20 3C300000 1FA400 count 428 > values 0 0 0 0 4C010 20 3C300000 1FA400 count 25965 > > which is ca. 14 errors per 1k reads, though maybe it's special - > non-MI/MI boundary (at 0x32E21400), reserved addresses (0x32E213Fx) etc. > > > The problems show themselves maybe in 5% or 10% of boots. How do you detect if the current boot was a "faulty" one? > > Well, now it appears more like 20%: e.g. in 41 system runs (soft reboots > only, no power-downs), I got problems 8 times. > > Obviously I can post the tester source if anyone is interested. I'd like to have a look there. I'm doing a lot of work on the imx8mp at the moment. I didn't have too many issues other than the ones caused by myself. There were however a few start/stop issues that are still on my list for further investigations. But I didn't observe bigger memory corruptions. So I think this ties into my earlier question of how you observe that the device is in a bad state. I'm mostly using the debix boards right now, so I could test if I can replicate the behavior there. Can you also share the kernel you are using? Best regards, Stefan > > > Generally ISP MI register read accesses which can be corrupted are: > - first 32 bits read in a given transfer, and additionally > - every 32 bits on a 32-byte boundary (addresses 0x....00, ...20 etc.). > > This means, in practice, on i.MX8MP only, RKISP1_CIF_MI_CTRL and > RKISP1_CIF_MI_MP_CB_SIZE_INIT (with the workaround). > > What is this 32-byte boundary? > > Writing is a bigger problem, though. > -- > Krzysztof "Chris" Hałasa > > Sieć Badawcza Łukasiewicz > Przemysłowy Instytut Automatyki i Pomiarów PIAP > Al. Jerozolimskie 202, 02-486 Warszawa > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-21 8:46 ` Stefan Klug @ 2025-07-21 13:16 ` Krzysztof Hałasa 2025-07-23 10:19 ` Stefan Klug 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-07-21 13:16 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Stefan, Stefan Klug <stefan.klug@ideasonboard.com> writes: >> > The problems show themselves maybe in 5% or 10% of boots. > > How do you detect if the current boot was a "faulty" one? I just try to run the tester. NOTE: make sure you don't run it against a sleeping ISP (clock disabled, reset etc). I just run the camera application in one session and my tester in another. Also, make sure the -m argument and the address are both valid for the ISP in use (0x32E1xxxx for the first MIPI/ISP and 0x32E2xxxx for the second). If it shows something like: # ./analyze_mi -m1 -s10000000 -a0x32E21400 -8 Using LDP Q*, Q*, [X*] instructions (2 * 128 bits) addr: 32E21400 32E21404 32E21408 32E2140C 32E21410 32E21414 32E21418 32E2141C ------------------------------------------------------------------------------ values 7A2001 20 3C240000 1FA400 0 0 0 3C2C0000 count 2242922 values 7A2001 20 3C300000 1FA400 0 0 0 3C2C0000 count 126 values 7A2001 20 3C300000 1FA400 0 0 0 3C380000 count 2106801 values 7A2001 20 3C3C0000 1FA400 0 0 0 3C380000 count 110 values 7A2001 20 3C3C0000 1FA400 0 0 0 3C440000 count 1982059 values 7A2001 20 3C0C0000 1FA400 0 0 0 3C440000 count 94 values 7A2001 20 3C0C0000 1FA400 0 0 0 3C140000 count 1871623 values 7A2001 20 3C180000 1FA400 0 0 0 3C140000 count 64 values 7A2001 20 3C180000 1FA400 0 0 0 3C200000 count 1796142 values 7A2001 20 3C240000 1FA400 0 0 0 3C200000 count 59 then all is good. OTOH if it's: # ./analyze_mi -m1 -s10000000 -a0x32E21400 -8 Using LDP Q*, Q*, [X*] instructions (2 * 128 bits) addr: 32E21400 32E21404 32E21408 32E2140C 32E21410 32E21414 32E21418 32E2141C ------------------------------------------------------------------------------ values 0 20 3C300000 1FA400 0 0 0 3C380000 count 139 values 7A2001 20 3C300000 1FA400 0 0 0 3C380000 count 2202205 values 7A2001 20 3C3C0000 1FA400 0 0 0 3C380000 count 108 values 7A2001 20 3C3C0000 1FA400 0 0 0 3C440000 count 2121036 values 0 20 3C3C0000 1FA400 0 0 0 3C440000 count 117 values 7A2001 20 3C0C0000 1FA400 0 0 0 3C440000 count 112 values 7A2001 20 3C0C0000 1FA400 0 0 0 3C140000 count 1997298 values 0 20 3C0C0000 1FA400 0 0 0 3C140000 count 88 values 7A2001 20 3C180000 1FA400 0 0 0 3C140000 count 106 values 7A2001 20 3C180000 1FA400 0 0 0 3C200000 count 1893775 values 0 20 3C180000 1FA400 0 0 0 3C200000 count 80 values 7A2001 20 3C240000 1FA400 0 0 0 3C200000 count 72 values 7A2001 20 3C240000 1FA400 0 0 0 3C2C0000 count 1784697 values 0 20 3C240000 1FA400 0 0 0 3C2C0000 count 65 values 7A2001 20 3C300000 1FA400 0 0 0 3C2C0000 count 67 values FD200 20 3C0C0000 1FA400 0 0 0 3C140000 count 4 values FD200 20 3C3C0000 1FA400 0 0 0 3C440000 count 5 values 10001 20 3C0C0000 1FA400 0 0 0 3C140000 count 3 values FD200 20 3C180000 1FA400 0 0 0 3C200000 count 6 values 10001 20 3C180000 1FA400 0 0 0 3C200000 count 2 values 3C200000 20 3C180000 1FA400 0 0 0 3C200000 count 1 values 3C380000 20 3C300000 1FA400 0 0 0 3C380000 count 3 values 10001 20 3C300000 1FA400 0 0 0 3C380000 count 5 values FD200 20 3C240000 1FA400 0 0 0 3C2C0000 count 1 values FD200 20 3C300000 1FA400 0 0 0 3C380000 count 3 values 3C2C0000 20 3C240000 1FA400 0 0 0 3C2C0000 count 1 values 10001 20 3C3C0000 1FA400 0 0 0 3C440000 count 1 then, well, something's not right with the register at 0x32E21400. The NXP-supplied docs on the ISP are not very helpful here, but maybe the Rockchip RK3288 ISP docs are (there is a PDF on rockchip.fr). It seems that the problem probably sits in the PVCI/AHB Slave -> CTRL -> "To all (ISP) modules" (I don't know how ISP registers are connected to the AHB/AXI on .MX8MP and it probably doesn't matter, the problem is somewhere between the "AHB Slave and the "all modules"). It appears "only" MI registers are affected, though. > Can you also share the kernel you are using? I'm currently using v6.15 with several unrelated patches. Apparently only this test patch could be relevant. BTW it won't have much effect on the userspace utility (and it's not needed for the utility, it's just for the driver and the camera application). However, those problems were present with NXP-supplied kernels, too. index 60c97bb7b18b..9530e653191a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c @@ -192,3 +192,80 @@ void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern, for (unsigned int i = 0; i < 4; ++i) output[i] = input[swap[pattern][i]]; } + +#define MIN_MATCHES 5 +#define READS 9 +u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr) +{ + if (addr >= RKISP1_CIF_MI_BASE && addr <= RKISP1_CIF_MI_MP_Y_PIC_SIZE) { + unsigned long flags; + spin_lock_irqsave(&rkisp1->reg_lock, flags); + + uint32_t v[READS]; + unsigned a, b, cnt; + // i.MX8MP ISP MI interface corrupts transfers from time to time + if (addr % 8) { // 0x...4 and 0x...C + uint32_t value; + addr -= 4; + // first value may be corrupted, we discard it in wzr register + asm volatile ("ldp wzr, %w0, [%x1]" "\n" + : "=r" (value) // output + : "r" (rkisp1->base_addr + addr)); // input + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); + return value; + } + if (addr % 16) { // 0x...8 + uint64_t value; + addr -= 8; // 64-bit: will read 0x...0 and 0x...8 + // first value may be corrupted, we discard it in xzr register + asm volatile ("ldp xzr, %x0, [%x1]" "\n" + : "=r" (value) // output + : "r" (rkisp1->base_addr + addr)); // input + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); + return value; // little endian: only least significant 32 bits + } + + // 0x...0 adreses are problematic: read multiple times + for (a = 0; a < ARRAY_SIZE(v); a++) + v[a] = readl(rkisp1->base_addr + addr); + for (a = 0; a < ARRAY_SIZE(v) - MIN_MATCHES + 1; a++) { + cnt = 0; + for (b = a; b < ARRAY_SIZE(v); b++) + if (v[b] == v[a]) { + cnt++; + if (cnt == MIN_MATCHES) { + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); + return v[a]; + } + } + } + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); + pr_warn("Error reading ISP MI register 0x%X, returning the last value 0x%X\n", addr, v[ARRAY_SIZE(v) - 1]); + return v[ARRAY_SIZE(v) - 1]; + } + + return readl(rkisp1->base_addr + addr); +} + +#define MAX_WRITES 5 +void rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val) +{ + if (addr >= RKISP1_CIF_MI_BASE && + addr <= RKISP1_CIF_MI_MP_Y_PIC_SIZE && + addr != RKISP1_CIF_MI_ICR /* write only */ && + addr != RKISP1_CIF_MI_INIT) { + for (unsigned cnt = 0; cnt < MAX_WRITES; cnt++) { + unsigned long flags; + spin_lock_irqsave(&rkisp1->reg_lock, flags); + writel(val, rkisp1->base_addr + addr); + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); + + if (rkisp1_read(rkisp1, addr) == val) + return; // succeeded + } + pr_warn("Error writing 0x%X to ISP MI register 0x%X\n", val, addr); + return; + } + + writel(val, rkisp1->base_addr + addr); +} index ca952fd0829b..21bab4a3e647 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -520,6 +520,7 @@ struct rkisp1_device { struct media_pipeline pipe; struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */ struct rkisp1_debug debug; + spinlock_t reg_lock; // used to serialize access to MI registers const struct rkisp1_info *info; int irqs[RKISP1_NUM_IRQS]; bool irqs_enabled; @@ -547,16 +548,8 @@ struct rkisp1_mbus_info { unsigned int direction; }; -static inline void -rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val) -{ - writel(val, rkisp1->base_addr + addr); -} - -static inline u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr) -{ - return readl(rkisp1->base_addr + addr); -} +u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr); +void rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val); /* * rkisp1_cap_enum_mbus_codes - A helper function that return the i'th supported mbus code diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index dc65a7924f8a..07f87b70151b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -611,6 +611,7 @@ static int rkisp1_probe(struct platform_device *pdev) return ret; mutex_init(&rkisp1->stream_lock); + spin_lock_init(&rkisp1->reg_lock); rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rkisp1->base_addr)) The (current working version of) analyze_mi.c, compile with -O2 -lpopt and perhaps with -W -Wall -Wno-sign-compare -Wno-missing-field-initializers -Wno-pointer-sign. There is also a regular read_test (a single register and the whole ISP MI, may use "alt read" workaround), and a write_test for a single register as well. // -*- mode: c; c-basic-offset: 4; tab-width: 4; -*- // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2025 Sieć Badawcza Łukasiewicz * - Przemysłowy Instytut Automatyki i Pomiarów PIAP * Written by Krzysztof Hałasa * * An i.MX8MP ISP MI register test utility v0.1 * Make sure the ISP is active at all times while running this program. */ #include <err.h> #include <fcntl.h> #include <popt.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #define ISP1_ADDR 0x32e10000 // must be page-aligned #define ISP2_ADDR 0x32e20000 #define ISP_SIZE 0x4000 // with holes #define MI_OFFSET 0x1400 #define MI_SIZE 0x200 #define MI_REGS (MI_SIZE / 4 /* 32-bit */) #define MAX_VALUES 128 // max different values for a register #define error(...) err(EXIT_FAILURE, __VA_ARGS__) #define errorx(...) errx(EXIT_FAILURE, __VA_ARGS__) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define REG(offset, name, dec) [((offset) - MI_OFFSET) / 4] {(name), (dec)} static const struct reg { const char *name; int dec; } reg_tab[MI_REGS] = { REG(0x1400, "mi_ctrl", 0), // Global control register REG(0x1404, "mi_init", 0), // Control register for address init and skip function (w) // Main picture REG(0x1408, "mi_mp_y_base_ad_init", 0), // Base address for Y component, JPEG or raw data REG(0x140C, "mi_mp_y_size_init", 1), // Size of Y component, JPEG or raw data REG(0x1410, "mi_mp_y_offs_cnt_init", 0), // Offset counter init value for Y, JPEG or raw data REG(0x1414, "mi_mp_y_offs_cnt_start", 0), // Offset counter start value for Y, JPEG or raw data REG(0x1418, "mi_mp_y_irq_offs_init", 0), // Fill level interrupt offset value for Y, JPEG or raw data REG(0x141C, "mi_mp_cb_base_ad_init", 0), // Base address for Cb component ring buffer REG(0x1420, "mi_mp_cb_size_init", 1), // Size of Cb component ring buffer REG(0x1424, "mi_mp_cb_offs_cnt_init", 0), // Offset counter init value for Cb component ring buffer REG(0x1428, "mi_mp_cb_offs_cnt_start", 0), // Offset counter start value for Cb component ring buffer REG(0x142C, "mi_mp_cr_base_ad_init", 0), // Base address for Cr component ring buffer REG(0x1430, "mi_mp_cr_size_init", 0), // Size of Cr component ring buffer REG(0x1434, "mi_mp_cr_offs_cnt_init", 0), // Offset counter init value for Cr component ring buffer REG(0x1438, "mi_mp_cr_offs_cnt_start", 0), // Offset counter start value for Cr component ring buffer #if 0 // Self picture REG(0x143C, "mi_sp_y_base_ad_init", 0), // Base address for Y component ring buffer REG(0x1440, "mi_sp_y_size_init", 1), // Size of Y component ring buffer REG(0x1444, "mi_sp_y_offs_cnt_init", 0), // Offset counter init value for Y component ring buffer REG(0x1448, "mi_sp_y_offs_cnt_start", 0), // Offset counter start value for Y component ring buffer REG(0x144C, "mi_sp_y_llength", 1), // Line length of Y component REG(0x1450, "mi_sp_cb_base_ad_init", 0), // Base address for Cb component ring buffer REG(0x1454, "mi_sp_cb_size_init", 1), // Size of Cb component ring buffer REG(0x1458, "mi_sp_cb_offs_cnt_init", 0), // Offset counter init value for Cb component ring buffer REG(0x145C, "mi_sp_cb_offs_cnt_start", 0), // Offset counter start value for Cb component ring buffer REG(0x1460, "mi_sp_cr_base_ad_init", 0), // Base address for Cr component ring buffer REG(0x1464, "mi_sp_cr_size_init", 1), // Size of Cr component ring buffer REG(0x1468, "mi_sp_cr_offs_cnt_init", 0), // Offset counter init value for Cr component ring buffer REG(0x146C, "mi_sp_cr_offs_cnt_start", 0), // Offset counter start value for Cr component ring buffer #endif REG(0x1470, "mi_byte_cnt", 0), // Counter value of JPEG or RAW data bytes REG(0x1474, "mi_ctrl_shd", 0), // global control internal shadow register // Main picture REG(0x1478, "mi_mp_y_base_ad_shd", 0), // Base address shadow register for Y component, JPEG or raw data ring buffer REG(0x147C, "mi_mp_y_size_shd", 1), // Size shadow register of Y component, JPEG or raw data REG(0x1480, "mi_mp_y_offs_cnt_shd", 0), // Current offset counter of Y component, JPEG or raw data ring buffer REG(0x1484, "mi_mp_y_irq_offs_shd", 0), // Shadow register of fill level interrupt offset value for Y component, JPEG or raw data REG(0x1488, "mi_mp_cb_base_ad_shd", 0), // Base address shadow register for Cb component ring buffer REG(0x148C, "mi_mp_cb_size_shd", 1), // Size shadow register of Cb component ring buffer REG(0x1490, "mi_mp_cb_offs_cnt_shd", 0), // Current offset counter of Cb component ring buffer REG(0x1494, "mi_mp_cr_base_ad_shd", 0), // Base address shadow register for Cr component ring buffer REG(0x1498, "mi_mp_cr_size_shd", 0), // Size shadow register of Cr component ring buffer REG(0x149C, "mi_mp_cr_offs_cnt_shd", 0), // Current offset counter of Cr component ring buffer #if 0 // Self picture REG(0x14A0, "mi_sp_y_base_ad_shd", 0), // Base address shadow register for Y component ring buffer REG(0x14A4, "mi_sp_y_size_shd", 1), // Size shadow register of Y component ring buffer REG(0x14A8, "mi_sp_y_offs_cnt_shd", 0), // Current offset counter of Y component ring buffer REG(0x14B0, "mi_sp_cb_base_ad_shd", 0), // Base address shadow register for Cb component ring buffer REG(0x14B4, "mi_sp_cb_size_shd", 1), // Size shadow register of Cb component ring buffer REG(0x14B8, "mi_sp_cb_offs_cnt_shd", 0), // Current offset counter of Cb component ring buffer REG(0x14BC, "mi_sp_cr_base_ad_shd", 0), // Base address shadow register for Cr component ring buffer REG(0x14C0, "mi_sp_cr_size_shd", 1), // Size shadow register of Cr component ring buffer REG(0x14C4, "mi_sp_cr_offs_cnt_shd", 0), // Current offset counter of Cr component ring buffer #endif REG(0x14C8, "mi_dma_y_pic_start_ad", 1), // Y component image start address REG(0x14CC, "mi_dma_y_pic_width", 1), // Y component image width REG(0x14D0, "mi_dma_y_llength", 1), // Y component original line length REG(0x14D4, "mi_dma_y_pic_size", 1), // Y component image size REG(0x14D8, "mi_dma_cb_pic_start_ad", 0), // Cb component image start address REG(0x14E8, "mi_dma_cr_pic_start_ad", 0), // Cr component image start address REG(0x14F8, "mi_imsc", 0), // Interrupt Mask (1: interrupt active, 0: interrupt masked) REG(0x14FC, "mi_ris", 0), // Raw Interrupt Status REG(0x1500, "mi_mis", 0), // Masked Interrupt Status REG(0x1504, "mi_icr", 0), // Interrupt Clear Register (w) REG(0x1508, "mi_isr", 0), // Interrupt Set Register (w) REG(0x150C, "mi_status", 0), // MI Status Register REG(0x1510, "mi_status_clr", 0), // MI Status Clear Register (w) REG(0x1514, "mi_sp_y_pic_width", 1), // Y component image width REG(0x1518, "mi_sp_y_pic_height", 1), // Y component image height REG(0x151C, "mi_sp_y_pic_size", 1), // Y component image size REG(0x1520, "mi_dma_ctrl", 0), // DMA control register REG(0x1524, "mi_dma_start", 0), // DMA start register (w) REG(0x1528, "mi_dma_status", 0), // DMA status register REG(0x152C, "mi_pixel_cnt", 0), // Counter value for defect pixel list // Main picture REG(0x1530, "mi_mp_y_base_ad_init2", 0), // Base address 2 (ping pong) for Y component, JPEG or raw data REG(0x1534, "mi_mp_cb_base_ad_init2", 0), // Base address 2 (pingpong) for Cb component REG(0x1538, "mi_mp_cr_base_ad_init2", 0), // Base address 2 (pingpong) for Cr component ring buffer #if 0 // Self picture REG(0x153C, "mi_sp_y_base_ad_init2", 0), // Base address 2 (ping pong) for Y component, JPEG or raw data REG(0x1540, "mi_sp_cb_base_ad_init2", 0), // Base address 2 (pingpong) for Cb component REG(0x1544, "mi_sp_cr_base_ad_init2", 0), // Base address 2 (pingpong) for Cr component ring buffer #endif REG(0x1548, "mi_reserved_1", 0), #ifdef ISP_MI_HANDSHAKE_NANO // never defined REG(0x154C, "mi_mp_handshake", 0), // MI mp handshake control for Nano handshake #else REG(0x154C, "mi_reserved_1_1", 0), #endif REG(0x1550, "mi_mp_y_llength", 1), // MI mp y llength for Nano handshake, REG(0x1554, "mi_mp_y_slice_offset", 0), // MI mp y slice offset for Nano handshake, REG(0x1558, "mi_mp_c_slice_offset", 0), // MI mp c slice offset for Nano handshare, REG(0x155C, "mi_output_align_format", 0), // MI output byte swap and LSB alignment control for Nano REG(0x1560, "mi_mp_output_fifo_size", 0), // MI mp output fifo control for Nano, REG(0x1564, "mi_mp_y_pic_width", 1), // MI mp y width pix for Nano handshake, REG(0x1568, "mi_mp_y_pic_height", 1), // MI mp y height pix for Nano handshake, REG(0x156C, "mi_mp_y_pic_size", 1), // MI mp y pix size for Nano handshare #ifdef ISP_MI_BP // not defined REG(0x1580, "mi_bp_ctrl", 0), REG(0x1584, "mi_bp_r_base_ad_shd", 0), REG(0x1588, "mi_bp_gr_base_ad_shd", 0), REG(0x158C, "mi_bp_gb_base_ad_shd", 0), REG(0x1590, "mi_bp_b_base_ad_shd", 0), REG(0x1594, "mi_bp_r_offs_cnt_shd", 0), REG(0x1598, "mi_bp_gr_offs_cnt_shd", 0), REG(0x159C, "mi_bp_gb_offs_cnt_shd", 0), REG(0x15A0, "mi_bp_b_offs_cnt_shd", 0), REG(0x15A4, "mi_bp_wr_offs_cnt_init", 0), REG(0x15A8, "mi_bp_wr_irq_offs_shd", 0), REG(0x15AC, "mi_bp_wr_irq_offs_init", 0), REG(0x15B0, "mi_bp_wr_size_shd", 0), REG(0x15B4, "mi_bp_wr_size_init", 0), REG(0x15B8, "mi_bp_wr_llength", 0), REG(0x15BC, "mi_bp_pic_width", 1), REG(0x15C0, "mi_bp_pic_height", 1), REG(0x15C4, "mi_bp_pic_size", 1), REG(0x15C8, "mi_bp_r_offs_cnt_start", 0), REG(0x15CC, "mi_bp_gr_offs_cnt_start", 0), REG(0x15D0, "mi_bp_gb_offs_cnt_start", 0), REG(0x15D4, "mi_bp_b_offs_cnt_start", 0), REG(0x15D8, "mi_bp_r_base_ad_init", 0), REG(0x15DC, "mi_bp_gr_base_ad_init", 0), REG(0x15E0, "mi_bp_gb_base_ad_init", 0), REG(0x15E4, "mi_bp_b_base_ad_init", 0), #endif REG(0x15E8, "mi_dma_y_raw_fmt", 0), REG(0x15EC, "mi_dma_y_raw_lval", 0) }; struct values { struct { uint32_t value; unsigned count; } data[MAX_VALUES]; unsigned data_count; }; static int alt_read, verbose, debug; static const struct reg *get_reg(uint32_t phys) { phys &= ISP_SIZE - 1; if (phys >= MI_OFFSET && phys < MI_OFFSET + MI_SIZE * 2 /* 2 copies */) return ®_tab[(phys - MI_OFFSET) / 4 /* 32-bit*/ % MI_REGS /* 2 copies */]; return NULL; } static void add_value(uint32_t phys, uint32_t value, struct values *values) { unsigned cnt; for (cnt = 0; cnt < values->data_count; cnt++) { if (values->data[cnt].value == value) { values->data[cnt].count++; break; } } if (cnt == values->data_count) { // not found yet if (values->data_count == MAX_VALUES) errorx("Too many register 0x%08X values", phys); values->data[cnt].value = value; values->data[cnt].count = 1; values->data_count++; } } static void show_values(uint32_t phys, const struct values *values) { const struct reg *reg = get_reg(phys); for (unsigned cnt = 0; cnt < values->data_count; cnt++) { printf("Register 0x%08X %-23s", phys, reg && reg->name ? reg->name : ""); if ((reg && reg->dec) || values->data[cnt].value < 10) printf(" value %10u count %u\n", values->data[cnt].value, values->data[cnt].count); else printf(" value 0x%08X count %u\n", values->data[cnt].value, values->data[cnt].count); } printf("\n"); } static uint32_t read_reg(const volatile uint32_t *virt) { if (alt_read) { if ((long)virt % 8) { // 32-bit LDP works with registers at xxx4 and xxxC, but corrupts xxx0 and xxx8 transfers. virt--; uint32_t value; asm volatile ("ldp wzr, %w0, [%x1]" "\n" : "=r" (value) // output : "r" (virt)); // input return value; } else { virt -= 2; uint64_t value; asm volatile ("ldp xzr, %x0, [%x1]" "\n" : "=r" (value) // output : "r" (virt)); // input return value; // little endian: only least significant 32 bits } } else return *(volatile uint32_t *)(virt); } static void __attribute__((noinline)) read_reg2(const volatile uint32_t *virt, void *v) { uint32_t r, s; asm volatile ("ldp %w0, %w1, [%x2]" "\n" : "=r" (r), "=r" (s) // output : "r" (virt)); // input memcpy(v, &r, sizeof(r)); // possibly corrupted memcpy(v + sizeof(r), &s, sizeof(s)); // possibly corrupted if virt starts on 8-byte boundary (valid for xxx4 and xxxC) } static void __attribute__((noinline)) read_reg4(const volatile uint32_t *virt, void *v) { uint64_t r, s; asm volatile ("ldp %x0, %x1, [%x2]" "\n" : "=r" (r), "=r" (s) // output : "r" (virt)); // input memcpy(v, &r, sizeof(r)); // first half possibly corrupted, corruption in second half at xxxC also possible memcpy(v + sizeof(r), &s, sizeof(s)); // first half possibly corrupted if virt starts on 16-byte boundary (valid for xxx8) } static void __attribute__((noinline)) read_reg8(const volatile uint32_t *virt, void *v) { _Float128 r, s; asm volatile ("ldp %q0, %q1, [%x2]" "\n" : "=w" (r), "=w" (s) // output : "r" (virt)); // input memcpy(v, &r, sizeof(r)); // possibly corrupted memcpy(v + sizeof(r), &s, sizeof(s)); // possibly corrupted if virt starts on 8-byte boundary (valid for xxx4 and xxxC) } static void test_all(int samples, uint32_t mi_phys, uint32_t *mi) { struct values values[MI_REGS] = {}; for (unsigned cnt = 0; cnt < samples; cnt++) { if (cnt % (samples / 10) == 0) printf("Sample %u\n", cnt); for (unsigned reg = 0; reg < MI_REGS; reg++) add_value(MI_OFFSET + reg * 4, read_reg(mi + reg), &values[reg]); } printf("\n"); for (unsigned reg = 0; reg < MI_REGS; reg++) { uint32_t reg_addr = mi_phys + reg * 4; if (values[reg].data_count && (verbose || values[reg].data[0].value || values[reg].data[0].count != samples)) show_values(reg_addr, &values[reg]); } } static void test_reg(int samples, uint32_t phys, uint32_t *virt, uint32_t write_mask) { if (!write_mask) { struct values values = {}; for (unsigned cnt = 0; cnt < samples; cnt++) { if (cnt % (samples / 10) == 0) printf("Sample %u\n", cnt); add_value(phys, read_reg(virt), &values); } printf("\n"); show_values(phys, &values); } else { const struct reg *reg = get_reg(phys); if (reg && reg->name) printf("Register 0x%08X %s: (all values in hex)\n", phys, reg->name); else printf("Register 0x%08X: (all values in hex)\n", phys); uint32_t value = 0, prev_value = 0; unsigned mismatch_count = 0; for (unsigned cnt = 0; cnt < samples; cnt++) { *virt = value; uint32_t new_values[9]; unsigned iter; for (iter = 0; iter < ARRAY_SIZE(new_values); iter++) new_values[iter] = read_reg(virt); for (iter = 0; iter < ARRAY_SIZE(new_values); iter++) if (new_values[iter] != value) break; if (iter != ARRAY_SIZE(new_values)) { printf("%8u:", cnt); printf(prev_value < 10 ? " %8u" : " %08X", prev_value); printf(value < 10 ? " WR %8u RD" : " WR %08X RD", value); for (unsigned iter = 0; iter < ARRAY_SIZE(new_values); iter++) if (new_values[iter] == value) printf(" valid"); else if (new_values[iter] == prev_value) printf(" previous"); else if (iter && new_values[iter] == new_values[iter - 1]) printf(" same"); else printf(new_values[iter] < 10 ? " %8u" : " %08X", new_values[iter]); putchar('\n'); mismatch_count++; } prev_value = value; value = random(); value ^= ((uint32_t)random()) << 16; value &= write_mask; } if (mismatch_count) printf("%u mismatches found\n", mismatch_count); else printf("No mismatches found\n"); } } static void test_reg_ldp(int samples, uint32_t phys, uint32_t *virt, unsigned words /* 32-bit*/) { if (phys & (words * 2 - 1)) errorx("Register address 0x%08X for 2 * %u-bit test is invalid", phys, words * 16); struct { uint32_t v[8]; // max unsigned count; } data[MAX_VALUES] = {}; unsigned data_count = 0; switch (words) { case 2: printf("Using LDP W*, W*, [X*] instructions (2 * 32 bits)\n"); break; case 4: printf("Using LDP X*, X*, [X*] instructions (2 * 64 bits)\n"); break; default: printf("Using LDP Q*, Q*, [X*] instructions (2 * 128 bits)\n"); } for (unsigned sample = 0; sample < samples; sample++) { uint32_t v[8]; switch (words) { case 2: read_reg2(virt, v); break; case 4: read_reg4(virt, v); break; default: read_reg8(virt, v); } unsigned cnt; for (cnt = 0; cnt < data_count; cnt++) { if (!memcmp(data[cnt].v, v, words * 4)) { data[cnt].count++; break; } } if (cnt == data_count) { // not found yet if (data_count == MAX_VALUES) errorx("Too many register 0x%08X values", phys); memcpy(data[cnt].v, v, words * 4); data[cnt].count = 1; data_count++; } } printf("addr: "); for (unsigned idx = 0; idx < words; idx++) printf(" %08X", phys + 4 * idx); printf("\n------"); for (unsigned idx = 0; idx < words; idx++) printf("---------"); putchar('\n'); for (unsigned cnt = 0; cnt < data_count; cnt++) { printf("values"); for (unsigned idx = 0; idx < words; idx++) printf(" %8X", data[cnt].v[idx]); printf(" count %u\n", data[cnt].count); } putchar('\n'); } int main(int argc, char **argv) { int samples = 100000, mipi = 0, reg_addr = 0, test_read = 0, test_read2 = 0, test_read4 = 0, test_read8 = 0, test_write = 0; long write_mask = 0xFFFFFFFF; struct poptOption options[] = { {"samples", 's', POPT_ARG_INT, &samples, 0, "sample count"}, {"mipi", 'm', POPT_ARG_INT, &mipi, 0, "MIPI channel"}, {"address", 'a', POPT_ARG_INT, ®_addr, 0, "ISP register address"}, {"read-test", 'r', POPT_ARG_NONE, &test_read, 0, "Perform a register read test"}, {"read-test2", '2', POPT_ARG_NONE, &test_read2, 0, "Perform a 2 * 32-bit register read test"}, {"read-test4", '4', POPT_ARG_NONE, &test_read4, 0, "Perform a 2 * 64-bit register read test"}, {"read-test8", '8', POPT_ARG_NONE, &test_read8, 0, "Perform a 2 * 128-bit register read test"}, {"write-test", 'w', POPT_ARG_NONE, &test_write, 0, "Perform a register write test"}, {"write-mask", 'M', POPT_ARG_LONG, &write_mask, 0, "Value mask for write test", "MASK"}, {"alt-read-mode", 'A', POPT_ARG_NONE, &alt_read, 0, "Alternate register read mode"}, {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "Verbose output"}, {"debug", 'd', POPT_ARG_NONE, &debug, 0, "Debug output"}, POPT_AUTOHELP POPT_TABLEEND }; poptContext context = poptGetContext(NULL, argc, (const char **)argv, options, 0); int i = poptGetNextOpt(context); if (i < -1) errorx("%s: %s\n", poptBadOption(context, POPT_BADOPTION_NOALIAS), poptStrerror(i)); if (poptPeekArg(context)) { poptPrintUsage(context, stderr, 0); exit(1); } poptFreeContext(context); if (samples <= 0) errorx("Invalid number of samples"); if (mipi != 0 && mipi != 1) errorx("Invalid MIPI interface index"); if (test_read + test_read2 + test_read4 + test_read8 + test_write > 1) errorx("Multiple tests requested"); if (write_mask < 1 || write_mask > 0xFFFFFFFF) errorx("Invalid write mask"); int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) error("Error opening /dev/mem"); uint32_t phys = mipi ? ISP2_ADDR : ISP1_ADDR; if (debug) printf("MIPI_ISP%u registers at 0x%X (size 0x%X)\n", mipi, phys, ISP_SIZE); void *virt = mmap(0, ISP_SIZE, test_write ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, mem_fd, phys); if (virt == MAP_FAILED) error("Mmap failed"); if (debug) printf("Mapped ISP registers at %p\n", virt); if (test_read || test_read2 || test_read4 || test_read8 || test_write) { if (reg_addr & 3 || reg_addr < phys || reg_addr >= phys + ISP_SIZE) errorx("Invalid ISP register address 0x%08X", reg_addr); virt += reg_addr - phys; if (debug) printf("Register 0x%X mapped at %p\n", reg_addr, virt); if (test_read) test_reg(samples, reg_addr, virt, 0); else if (test_read2) test_reg_ldp(samples, reg_addr, virt, 2); else if (test_read4) test_reg_ldp(samples, reg_addr, virt, 4); else if (test_read8) test_reg_ldp(samples, reg_addr, virt, 8); else test_reg(samples, reg_addr, virt, write_mask); } else test_all(samples, phys + MI_OFFSET, virt + MI_OFFSET); return 0; } -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-21 13:16 ` Krzysztof Hałasa @ 2025-07-23 10:19 ` Stefan Klug 2025-07-23 12:06 ` Krzysztof Hałasa 2025-07-24 8:21 ` Krzysztof Hałasa 0 siblings, 2 replies; 23+ messages in thread From: Stefan Klug @ 2025-07-23 10:19 UTC (permalink / raw) To: Krzysztof Hałasa Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Krzysztof, Quoting Krzysztof Hałasa (2025-07-21 15:16:28) > Hi Stefan, > > Stefan Klug <stefan.klug@ideasonboard.com> writes: > > >> > The problems show themselves maybe in 5% or 10% of boots. > > > > How do you detect if the current boot was a "faulty" one? > > I just try to run the tester. Just a quick heads up. I ran the tester and so far no unexpected results. I'll run it from time to time after a reboot to see if I ever hit that condition. How does your device tree look like? Any chance the ISP is clocked for overdrive but the device is not or something similar? Although I have a hard time imagining how that would lead to such effects. Best regards, Stefan > > NOTE: make sure you don't run it against a sleeping ISP (clock disabled, > reset etc). I just run the camera application in one session and my > tester in another. Also, make sure the -m argument and the address are > both valid for the ISP in use (0x32E1xxxx for the first MIPI/ISP and > 0x32E2xxxx for the second). > > If it shows something like: > > # ./analyze_mi -m1 -s10000000 -a0x32E21400 -8 > Using LDP Q*, Q*, [X*] instructions (2 * 128 bits) > addr: 32E21400 32E21404 32E21408 32E2140C 32E21410 32E21414 32E21418 32E2141C > ------------------------------------------------------------------------------ > values 7A2001 20 3C240000 1FA400 0 0 0 3C2C0000 count 2242922 > values 7A2001 20 3C300000 1FA400 0 0 0 3C2C0000 count 126 > values 7A2001 20 3C300000 1FA400 0 0 0 3C380000 count 2106801 > values 7A2001 20 3C3C0000 1FA400 0 0 0 3C380000 count 110 > values 7A2001 20 3C3C0000 1FA400 0 0 0 3C440000 count 1982059 > values 7A2001 20 3C0C0000 1FA400 0 0 0 3C440000 count 94 > values 7A2001 20 3C0C0000 1FA400 0 0 0 3C140000 count 1871623 > values 7A2001 20 3C180000 1FA400 0 0 0 3C140000 count 64 > values 7A2001 20 3C180000 1FA400 0 0 0 3C200000 count 1796142 > values 7A2001 20 3C240000 1FA400 0 0 0 3C200000 count 59 > > then all is good. > > OTOH if it's: > # ./analyze_mi -m1 -s10000000 -a0x32E21400 -8 > Using LDP Q*, Q*, [X*] instructions (2 * 128 bits) > addr: 32E21400 32E21404 32E21408 32E2140C 32E21410 32E21414 32E21418 32E2141C > ------------------------------------------------------------------------------ > values 0 20 3C300000 1FA400 0 0 0 3C380000 count 139 > values 7A2001 20 3C300000 1FA400 0 0 0 3C380000 count 2202205 > values 7A2001 20 3C3C0000 1FA400 0 0 0 3C380000 count 108 > values 7A2001 20 3C3C0000 1FA400 0 0 0 3C440000 count 2121036 > values 0 20 3C3C0000 1FA400 0 0 0 3C440000 count 117 > values 7A2001 20 3C0C0000 1FA400 0 0 0 3C440000 count 112 > values 7A2001 20 3C0C0000 1FA400 0 0 0 3C140000 count 1997298 > values 0 20 3C0C0000 1FA400 0 0 0 3C140000 count 88 > values 7A2001 20 3C180000 1FA400 0 0 0 3C140000 count 106 > values 7A2001 20 3C180000 1FA400 0 0 0 3C200000 count 1893775 > values 0 20 3C180000 1FA400 0 0 0 3C200000 count 80 > values 7A2001 20 3C240000 1FA400 0 0 0 3C200000 count 72 > values 7A2001 20 3C240000 1FA400 0 0 0 3C2C0000 count 1784697 > values 0 20 3C240000 1FA400 0 0 0 3C2C0000 count 65 > values 7A2001 20 3C300000 1FA400 0 0 0 3C2C0000 count 67 > values FD200 20 3C0C0000 1FA400 0 0 0 3C140000 count 4 > values FD200 20 3C3C0000 1FA400 0 0 0 3C440000 count 5 > values 10001 20 3C0C0000 1FA400 0 0 0 3C140000 count 3 > values FD200 20 3C180000 1FA400 0 0 0 3C200000 count 6 > values 10001 20 3C180000 1FA400 0 0 0 3C200000 count 2 > values 3C200000 20 3C180000 1FA400 0 0 0 3C200000 count 1 > values 3C380000 20 3C300000 1FA400 0 0 0 3C380000 count 3 > values 10001 20 3C300000 1FA400 0 0 0 3C380000 count 5 > values FD200 20 3C240000 1FA400 0 0 0 3C2C0000 count 1 > values FD200 20 3C300000 1FA400 0 0 0 3C380000 count 3 > values 3C2C0000 20 3C240000 1FA400 0 0 0 3C2C0000 count 1 > values 10001 20 3C3C0000 1FA400 0 0 0 3C440000 count 1 > > then, well, something's not right with the register at 0x32E21400. > > The NXP-supplied docs on the ISP are not very helpful here, but maybe > the Rockchip RK3288 ISP docs are (there is a PDF on rockchip.fr). > It seems that the problem probably sits in the PVCI/AHB Slave -> CTRL -> > "To all (ISP) modules" (I don't know how ISP registers are connected to > the AHB/AXI on .MX8MP and it probably doesn't matter, the problem is > somewhere between the "AHB Slave and the "all modules"). > It appears "only" MI registers are affected, though. > > > Can you also share the kernel you are using? > > I'm currently using v6.15 with several unrelated patches. Apparently > only this test patch could be relevant. BTW it won't have much effect > on the userspace utility (and it's not needed for the utility, it's just > for the driver and the camera application). > > However, those problems were present with NXP-supplied kernels, too. > > index 60c97bb7b18b..9530e653191a 100644 > --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c > +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c > @@ -192,3 +192,80 @@ void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern, > for (unsigned int i = 0; i < 4; ++i) > output[i] = input[swap[pattern][i]]; > } > + > +#define MIN_MATCHES 5 > +#define READS 9 > +u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr) > +{ > + if (addr >= RKISP1_CIF_MI_BASE && addr <= RKISP1_CIF_MI_MP_Y_PIC_SIZE) { > + unsigned long flags; > + spin_lock_irqsave(&rkisp1->reg_lock, flags); > + > + uint32_t v[READS]; > + unsigned a, b, cnt; > + // i.MX8MP ISP MI interface corrupts transfers from time to time > + if (addr % 8) { // 0x...4 and 0x...C > + uint32_t value; > + addr -= 4; > + // first value may be corrupted, we discard it in wzr register > + asm volatile ("ldp wzr, %w0, [%x1]" "\n" > + : "=r" (value) // output > + : "r" (rkisp1->base_addr + addr)); // input > + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); > + return value; > + } > + if (addr % 16) { // 0x...8 > + uint64_t value; > + addr -= 8; // 64-bit: will read 0x...0 and 0x...8 > + // first value may be corrupted, we discard it in xzr register > + asm volatile ("ldp xzr, %x0, [%x1]" "\n" > + : "=r" (value) // output > + : "r" (rkisp1->base_addr + addr)); // input > + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); > + return value; // little endian: only least significant 32 bits > + } > + > + // 0x...0 adreses are problematic: read multiple times > + for (a = 0; a < ARRAY_SIZE(v); a++) > + v[a] = readl(rkisp1->base_addr + addr); > + for (a = 0; a < ARRAY_SIZE(v) - MIN_MATCHES + 1; a++) { > + cnt = 0; > + for (b = a; b < ARRAY_SIZE(v); b++) > + if (v[b] == v[a]) { > + cnt++; > + if (cnt == MIN_MATCHES) { > + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); > + return v[a]; > + } > + } > + } > + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); > + pr_warn("Error reading ISP MI register 0x%X, returning the last value 0x%X\n", addr, v[ARRAY_SIZE(v) - 1]); > + return v[ARRAY_SIZE(v) - 1]; > + } > + > + return readl(rkisp1->base_addr + addr); > +} > + > +#define MAX_WRITES 5 > +void rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val) > +{ > + if (addr >= RKISP1_CIF_MI_BASE && > + addr <= RKISP1_CIF_MI_MP_Y_PIC_SIZE && > + addr != RKISP1_CIF_MI_ICR /* write only */ && > + addr != RKISP1_CIF_MI_INIT) { > + for (unsigned cnt = 0; cnt < MAX_WRITES; cnt++) { > + unsigned long flags; > + spin_lock_irqsave(&rkisp1->reg_lock, flags); > + writel(val, rkisp1->base_addr + addr); > + spin_unlock_irqrestore(&rkisp1->reg_lock, flags); > + > + if (rkisp1_read(rkisp1, addr) == val) > + return; // succeeded > + } > + pr_warn("Error writing 0x%X to ISP MI register 0x%X\n", val, addr); > + return; > + } > + > + writel(val, rkisp1->base_addr + addr); > +} > > index ca952fd0829b..21bab4a3e647 100644 > --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h > +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h > @@ -520,6 +520,7 @@ struct rkisp1_device { > struct media_pipeline pipe; > struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */ > struct rkisp1_debug debug; > + spinlock_t reg_lock; // used to serialize access to MI registers > const struct rkisp1_info *info; > int irqs[RKISP1_NUM_IRQS]; > bool irqs_enabled; > @@ -547,16 +548,8 @@ struct rkisp1_mbus_info { > unsigned int direction; > }; > > -static inline void > -rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val) > -{ > - writel(val, rkisp1->base_addr + addr); > -} > - > -static inline u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr) > -{ > - return readl(rkisp1->base_addr + addr); > -} > +u32 rkisp1_read(struct rkisp1_device *rkisp1, unsigned int addr); > +void rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val); > > /* > * rkisp1_cap_enum_mbus_codes - A helper function that return the i'th supported mbus code > diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c > index dc65a7924f8a..07f87b70151b 100644 > --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c > +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c > @@ -611,6 +611,7 @@ static int rkisp1_probe(struct platform_device *pdev) > return ret; > > mutex_init(&rkisp1->stream_lock); > + spin_lock_init(&rkisp1->reg_lock); > > rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0); > if (IS_ERR(rkisp1->base_addr)) > > > The (current working version of) analyze_mi.c, compile with -O2 -lpopt > and perhaps with -W -Wall -Wno-sign-compare > -Wno-missing-field-initializers -Wno-pointer-sign. > There is also a regular read_test (a single register and the whole ISP > MI, may use "alt read" workaround), and a write_test for a single > register as well. > > // -*- mode: c; c-basic-offset: 4; tab-width: 4; -*- > // SPDX-License-Identifier: GPL-2.0 > /* > * Copyright (C) 2025 Sieć Badawcza Łukasiewicz > * - Przemysłowy Instytut Automatyki i Pomiarów PIAP > * Written by Krzysztof Hałasa > * > * An i.MX8MP ISP MI register test utility v0.1 > * Make sure the ISP is active at all times while running this program. > */ > > #include <err.h> > #include <fcntl.h> > #include <popt.h> > #include <stdint.h> > #include <stdlib.h> > #include <string.h> > #include <sys/mman.h> > > #define ISP1_ADDR 0x32e10000 // must be page-aligned > #define ISP2_ADDR 0x32e20000 > #define ISP_SIZE 0x4000 // with holes > #define MI_OFFSET 0x1400 > #define MI_SIZE 0x200 > #define MI_REGS (MI_SIZE / 4 /* 32-bit */) > #define MAX_VALUES 128 // max different values for a register > > #define error(...) err(EXIT_FAILURE, __VA_ARGS__) > #define errorx(...) errx(EXIT_FAILURE, __VA_ARGS__) > #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) > #define REG(offset, name, dec) [((offset) - MI_OFFSET) / 4] {(name), (dec)} > > static const struct reg { > const char *name; > int dec; > } reg_tab[MI_REGS] = { > REG(0x1400, "mi_ctrl", 0), // Global control register > REG(0x1404, "mi_init", 0), // Control register for address init and skip function (w) > > // Main picture > REG(0x1408, "mi_mp_y_base_ad_init", 0), // Base address for Y component, JPEG or raw data > REG(0x140C, "mi_mp_y_size_init", 1), // Size of Y component, JPEG or raw data > REG(0x1410, "mi_mp_y_offs_cnt_init", 0), // Offset counter init value for Y, JPEG or raw data > REG(0x1414, "mi_mp_y_offs_cnt_start", 0), // Offset counter start value for Y, JPEG or raw data > REG(0x1418, "mi_mp_y_irq_offs_init", 0), // Fill level interrupt offset value for Y, JPEG or raw data > REG(0x141C, "mi_mp_cb_base_ad_init", 0), // Base address for Cb component ring buffer > REG(0x1420, "mi_mp_cb_size_init", 1), // Size of Cb component ring buffer > REG(0x1424, "mi_mp_cb_offs_cnt_init", 0), // Offset counter init value for Cb component ring buffer > REG(0x1428, "mi_mp_cb_offs_cnt_start", 0), // Offset counter start value for Cb component ring buffer > REG(0x142C, "mi_mp_cr_base_ad_init", 0), // Base address for Cr component ring buffer > REG(0x1430, "mi_mp_cr_size_init", 0), // Size of Cr component ring buffer > REG(0x1434, "mi_mp_cr_offs_cnt_init", 0), // Offset counter init value for Cr component ring buffer > REG(0x1438, "mi_mp_cr_offs_cnt_start", 0), // Offset counter start value for Cr component ring buffer > > #if 0 > // Self picture > REG(0x143C, "mi_sp_y_base_ad_init", 0), // Base address for Y component ring buffer > REG(0x1440, "mi_sp_y_size_init", 1), // Size of Y component ring buffer > REG(0x1444, "mi_sp_y_offs_cnt_init", 0), // Offset counter init value for Y component ring buffer > REG(0x1448, "mi_sp_y_offs_cnt_start", 0), // Offset counter start value for Y component ring buffer > REG(0x144C, "mi_sp_y_llength", 1), // Line length of Y component > REG(0x1450, "mi_sp_cb_base_ad_init", 0), // Base address for Cb component ring buffer > REG(0x1454, "mi_sp_cb_size_init", 1), // Size of Cb component ring buffer > REG(0x1458, "mi_sp_cb_offs_cnt_init", 0), // Offset counter init value for Cb component ring buffer > REG(0x145C, "mi_sp_cb_offs_cnt_start", 0), // Offset counter start value for Cb component ring buffer > REG(0x1460, "mi_sp_cr_base_ad_init", 0), // Base address for Cr component ring buffer > REG(0x1464, "mi_sp_cr_size_init", 1), // Size of Cr component ring buffer > REG(0x1468, "mi_sp_cr_offs_cnt_init", 0), // Offset counter init value for Cr component ring buffer > REG(0x146C, "mi_sp_cr_offs_cnt_start", 0), // Offset counter start value for Cr component ring buffer > #endif > > REG(0x1470, "mi_byte_cnt", 0), // Counter value of JPEG or RAW data bytes > REG(0x1474, "mi_ctrl_shd", 0), // global control internal shadow register > > // Main picture > REG(0x1478, "mi_mp_y_base_ad_shd", 0), // Base address shadow register for Y component, JPEG or raw data ring buffer > REG(0x147C, "mi_mp_y_size_shd", 1), // Size shadow register of Y component, JPEG or raw data > REG(0x1480, "mi_mp_y_offs_cnt_shd", 0), // Current offset counter of Y component, JPEG or raw data ring buffer > REG(0x1484, "mi_mp_y_irq_offs_shd", 0), // Shadow register of fill level interrupt offset value for Y component, JPEG or raw data > REG(0x1488, "mi_mp_cb_base_ad_shd", 0), // Base address shadow register for Cb component ring buffer > REG(0x148C, "mi_mp_cb_size_shd", 1), // Size shadow register of Cb component ring buffer > REG(0x1490, "mi_mp_cb_offs_cnt_shd", 0), // Current offset counter of Cb component ring buffer > REG(0x1494, "mi_mp_cr_base_ad_shd", 0), // Base address shadow register for Cr component ring buffer > REG(0x1498, "mi_mp_cr_size_shd", 0), // Size shadow register of Cr component ring buffer > REG(0x149C, "mi_mp_cr_offs_cnt_shd", 0), // Current offset counter of Cr component ring buffer > > #if 0 > // Self picture > REG(0x14A0, "mi_sp_y_base_ad_shd", 0), // Base address shadow register for Y component ring buffer > REG(0x14A4, "mi_sp_y_size_shd", 1), // Size shadow register of Y component ring buffer > REG(0x14A8, "mi_sp_y_offs_cnt_shd", 0), // Current offset counter of Y component ring buffer > > REG(0x14B0, "mi_sp_cb_base_ad_shd", 0), // Base address shadow register for Cb component ring buffer > REG(0x14B4, "mi_sp_cb_size_shd", 1), // Size shadow register of Cb component ring buffer > REG(0x14B8, "mi_sp_cb_offs_cnt_shd", 0), // Current offset counter of Cb component ring buffer > REG(0x14BC, "mi_sp_cr_base_ad_shd", 0), // Base address shadow register for Cr component ring buffer > REG(0x14C0, "mi_sp_cr_size_shd", 1), // Size shadow register of Cr component ring buffer > REG(0x14C4, "mi_sp_cr_offs_cnt_shd", 0), // Current offset counter of Cr component ring buffer > #endif > > REG(0x14C8, "mi_dma_y_pic_start_ad", 1), // Y component image start address > REG(0x14CC, "mi_dma_y_pic_width", 1), // Y component image width > REG(0x14D0, "mi_dma_y_llength", 1), // Y component original line length > REG(0x14D4, "mi_dma_y_pic_size", 1), // Y component image size > REG(0x14D8, "mi_dma_cb_pic_start_ad", 0), // Cb component image start address > REG(0x14E8, "mi_dma_cr_pic_start_ad", 0), // Cr component image start address > > REG(0x14F8, "mi_imsc", 0), // Interrupt Mask (1: interrupt active, 0: interrupt masked) > REG(0x14FC, "mi_ris", 0), // Raw Interrupt Status > REG(0x1500, "mi_mis", 0), // Masked Interrupt Status > REG(0x1504, "mi_icr", 0), // Interrupt Clear Register (w) > REG(0x1508, "mi_isr", 0), // Interrupt Set Register (w) > > REG(0x150C, "mi_status", 0), // MI Status Register > REG(0x1510, "mi_status_clr", 0), // MI Status Clear Register (w) > REG(0x1514, "mi_sp_y_pic_width", 1), // Y component image width > REG(0x1518, "mi_sp_y_pic_height", 1), // Y component image height > REG(0x151C, "mi_sp_y_pic_size", 1), // Y component image size > REG(0x1520, "mi_dma_ctrl", 0), // DMA control register > REG(0x1524, "mi_dma_start", 0), // DMA start register (w) > REG(0x1528, "mi_dma_status", 0), // DMA status register > REG(0x152C, "mi_pixel_cnt", 0), // Counter value for defect pixel list > > // Main picture > REG(0x1530, "mi_mp_y_base_ad_init2", 0), // Base address 2 (ping pong) for Y component, JPEG or raw data > REG(0x1534, "mi_mp_cb_base_ad_init2", 0), // Base address 2 (pingpong) for Cb component > REG(0x1538, "mi_mp_cr_base_ad_init2", 0), // Base address 2 (pingpong) for Cr component ring buffer > > #if 0 > // Self picture > REG(0x153C, "mi_sp_y_base_ad_init2", 0), // Base address 2 (ping pong) for Y component, JPEG or raw data > REG(0x1540, "mi_sp_cb_base_ad_init2", 0), // Base address 2 (pingpong) for Cb component > REG(0x1544, "mi_sp_cr_base_ad_init2", 0), // Base address 2 (pingpong) for Cr component ring buffer > #endif > > REG(0x1548, "mi_reserved_1", 0), > #ifdef ISP_MI_HANDSHAKE_NANO // never defined > REG(0x154C, "mi_mp_handshake", 0), // MI mp handshake control for Nano handshake > #else > REG(0x154C, "mi_reserved_1_1", 0), > #endif > REG(0x1550, "mi_mp_y_llength", 1), // MI mp y llength for Nano handshake, > REG(0x1554, "mi_mp_y_slice_offset", 0), // MI mp y slice offset for Nano handshake, > REG(0x1558, "mi_mp_c_slice_offset", 0), // MI mp c slice offset for Nano handshare, > REG(0x155C, "mi_output_align_format", 0), // MI output byte swap and LSB alignment control for Nano > REG(0x1560, "mi_mp_output_fifo_size", 0), // MI mp output fifo control for Nano, > REG(0x1564, "mi_mp_y_pic_width", 1), // MI mp y width pix for Nano handshake, > REG(0x1568, "mi_mp_y_pic_height", 1), // MI mp y height pix for Nano handshake, > REG(0x156C, "mi_mp_y_pic_size", 1), // MI mp y pix size for Nano handshare > > #ifdef ISP_MI_BP // not defined > REG(0x1580, "mi_bp_ctrl", 0), > REG(0x1584, "mi_bp_r_base_ad_shd", 0), > REG(0x1588, "mi_bp_gr_base_ad_shd", 0), > REG(0x158C, "mi_bp_gb_base_ad_shd", 0), > REG(0x1590, "mi_bp_b_base_ad_shd", 0), > REG(0x1594, "mi_bp_r_offs_cnt_shd", 0), > REG(0x1598, "mi_bp_gr_offs_cnt_shd", 0), > REG(0x159C, "mi_bp_gb_offs_cnt_shd", 0), > REG(0x15A0, "mi_bp_b_offs_cnt_shd", 0), > REG(0x15A4, "mi_bp_wr_offs_cnt_init", 0), > REG(0x15A8, "mi_bp_wr_irq_offs_shd", 0), > REG(0x15AC, "mi_bp_wr_irq_offs_init", 0), > REG(0x15B0, "mi_bp_wr_size_shd", 0), > REG(0x15B4, "mi_bp_wr_size_init", 0), > REG(0x15B8, "mi_bp_wr_llength", 0), > REG(0x15BC, "mi_bp_pic_width", 1), > REG(0x15C0, "mi_bp_pic_height", 1), > REG(0x15C4, "mi_bp_pic_size", 1), > REG(0x15C8, "mi_bp_r_offs_cnt_start", 0), > REG(0x15CC, "mi_bp_gr_offs_cnt_start", 0), > REG(0x15D0, "mi_bp_gb_offs_cnt_start", 0), > REG(0x15D4, "mi_bp_b_offs_cnt_start", 0), > REG(0x15D8, "mi_bp_r_base_ad_init", 0), > REG(0x15DC, "mi_bp_gr_base_ad_init", 0), > REG(0x15E0, "mi_bp_gb_base_ad_init", 0), > REG(0x15E4, "mi_bp_b_base_ad_init", 0), > #endif > REG(0x15E8, "mi_dma_y_raw_fmt", 0), > REG(0x15EC, "mi_dma_y_raw_lval", 0) > }; > > struct values { > struct { > uint32_t value; > unsigned count; > } data[MAX_VALUES]; > unsigned data_count; > }; > > static int alt_read, verbose, debug; > > static const struct reg *get_reg(uint32_t phys) > { > phys &= ISP_SIZE - 1; > if (phys >= MI_OFFSET && phys < MI_OFFSET + MI_SIZE * 2 /* 2 copies */) > return ®_tab[(phys - MI_OFFSET) / 4 /* 32-bit*/ % MI_REGS /* 2 copies */]; > return NULL; > } > > static void add_value(uint32_t phys, uint32_t value, struct values *values) > { > unsigned cnt; > for (cnt = 0; cnt < values->data_count; cnt++) { > if (values->data[cnt].value == value) { > values->data[cnt].count++; > break; > } > } > > if (cnt == values->data_count) { // not found yet > if (values->data_count == MAX_VALUES) > errorx("Too many register 0x%08X values", phys); > values->data[cnt].value = value; > values->data[cnt].count = 1; > values->data_count++; > } > } > > static void show_values(uint32_t phys, const struct values *values) > { > const struct reg *reg = get_reg(phys); > > for (unsigned cnt = 0; cnt < values->data_count; cnt++) { > printf("Register 0x%08X %-23s", phys, reg && reg->name ? reg->name : ""); > if ((reg && reg->dec) || values->data[cnt].value < 10) > printf(" value %10u count %u\n", values->data[cnt].value, values->data[cnt].count); > else > printf(" value 0x%08X count %u\n", values->data[cnt].value, values->data[cnt].count); > } > printf("\n"); > } > > static uint32_t read_reg(const volatile uint32_t *virt) > { > if (alt_read) { > if ((long)virt % 8) { > // 32-bit LDP works with registers at xxx4 and xxxC, but corrupts xxx0 and xxx8 transfers. > virt--; > uint32_t value; > asm volatile ("ldp wzr, %w0, [%x1]" "\n" > : "=r" (value) // output > : "r" (virt)); // input > return value; > } else { > virt -= 2; > uint64_t value; > asm volatile ("ldp xzr, %x0, [%x1]" "\n" > : "=r" (value) // output > : "r" (virt)); // input > return value; // little endian: only least significant 32 bits > } > } else > return *(volatile uint32_t *)(virt); > } > > static void __attribute__((noinline)) read_reg2(const volatile uint32_t *virt, void *v) > { > uint32_t r, s; > asm volatile ("ldp %w0, %w1, [%x2]" "\n" > : "=r" (r), "=r" (s) // output > : "r" (virt)); // input > memcpy(v, &r, sizeof(r)); // possibly corrupted > memcpy(v + sizeof(r), &s, sizeof(s)); // possibly corrupted if virt starts on 8-byte boundary (valid for xxx4 and xxxC) > } > > static void __attribute__((noinline)) read_reg4(const volatile uint32_t *virt, void *v) > { > uint64_t r, s; > asm volatile ("ldp %x0, %x1, [%x2]" "\n" > : "=r" (r), "=r" (s) // output > : "r" (virt)); // input > memcpy(v, &r, sizeof(r)); // first half possibly corrupted, corruption in second half at xxxC also possible > memcpy(v + sizeof(r), &s, sizeof(s)); // first half possibly corrupted if virt starts on 16-byte boundary (valid for xxx8) > } > > static void __attribute__((noinline)) read_reg8(const volatile uint32_t *virt, void *v) > { > _Float128 r, s; > asm volatile ("ldp %q0, %q1, [%x2]" "\n" > : "=w" (r), "=w" (s) // output > : "r" (virt)); // input > memcpy(v, &r, sizeof(r)); // possibly corrupted > memcpy(v + sizeof(r), &s, sizeof(s)); // possibly corrupted if virt starts on 8-byte boundary (valid for xxx4 and xxxC) > } > > static void test_all(int samples, uint32_t mi_phys, uint32_t *mi) > { > struct values values[MI_REGS] = {}; > for (unsigned cnt = 0; cnt < samples; cnt++) { > if (cnt % (samples / 10) == 0) > printf("Sample %u\n", cnt); > for (unsigned reg = 0; reg < MI_REGS; reg++) > add_value(MI_OFFSET + reg * 4, read_reg(mi + reg), &values[reg]); > } > printf("\n"); > > for (unsigned reg = 0; reg < MI_REGS; reg++) { > uint32_t reg_addr = mi_phys + reg * 4; > > if (values[reg].data_count && (verbose || values[reg].data[0].value || values[reg].data[0].count != samples)) > show_values(reg_addr, &values[reg]); > } > } > > static void test_reg(int samples, uint32_t phys, uint32_t *virt, uint32_t write_mask) > { > if (!write_mask) { > struct values values = {}; > for (unsigned cnt = 0; cnt < samples; cnt++) { > if (cnt % (samples / 10) == 0) > printf("Sample %u\n", cnt); > add_value(phys, read_reg(virt), &values); > } > printf("\n"); > > show_values(phys, &values); > } else { > const struct reg *reg = get_reg(phys); > if (reg && reg->name) > printf("Register 0x%08X %s: (all values in hex)\n", phys, reg->name); > else > printf("Register 0x%08X: (all values in hex)\n", phys); > > uint32_t value = 0, prev_value = 0; > unsigned mismatch_count = 0; > for (unsigned cnt = 0; cnt < samples; cnt++) { > *virt = value; > uint32_t new_values[9]; > unsigned iter; > > for (iter = 0; iter < ARRAY_SIZE(new_values); iter++) > new_values[iter] = read_reg(virt); > > for (iter = 0; iter < ARRAY_SIZE(new_values); iter++) > if (new_values[iter] != value) > break; > > if (iter != ARRAY_SIZE(new_values)) { > printf("%8u:", cnt); > printf(prev_value < 10 ? " %8u" : " %08X", prev_value); > printf(value < 10 ? " WR %8u RD" : " WR %08X RD", value); > for (unsigned iter = 0; iter < ARRAY_SIZE(new_values); iter++) > if (new_values[iter] == value) > printf(" valid"); > else if (new_values[iter] == prev_value) > printf(" previous"); > else if (iter && new_values[iter] == new_values[iter - 1]) > printf(" same"); > else > printf(new_values[iter] < 10 ? " %8u" : " %08X", new_values[iter]); > putchar('\n'); > mismatch_count++; > } > prev_value = value; > value = random(); > value ^= ((uint32_t)random()) << 16; > value &= write_mask; > } > if (mismatch_count) > printf("%u mismatches found\n", mismatch_count); > else > printf("No mismatches found\n"); > } > } > > static void test_reg_ldp(int samples, uint32_t phys, uint32_t *virt, unsigned words /* 32-bit*/) > { > if (phys & (words * 2 - 1)) > errorx("Register address 0x%08X for 2 * %u-bit test is invalid", phys, words * 16); > > struct { > uint32_t v[8]; // max > unsigned count; > } data[MAX_VALUES] = {}; > unsigned data_count = 0; > > switch (words) { > case 2: printf("Using LDP W*, W*, [X*] instructions (2 * 32 bits)\n"); break; > case 4: printf("Using LDP X*, X*, [X*] instructions (2 * 64 bits)\n"); break; > default: printf("Using LDP Q*, Q*, [X*] instructions (2 * 128 bits)\n"); > } > > for (unsigned sample = 0; sample < samples; sample++) { > uint32_t v[8]; > > switch (words) { > case 2: read_reg2(virt, v); break; > case 4: read_reg4(virt, v); break; > default: read_reg8(virt, v); > } > > unsigned cnt; > for (cnt = 0; cnt < data_count; cnt++) { > if (!memcmp(data[cnt].v, v, words * 4)) { > data[cnt].count++; > break; > } > } > > if (cnt == data_count) { // not found yet > if (data_count == MAX_VALUES) > errorx("Too many register 0x%08X values", phys); > memcpy(data[cnt].v, v, words * 4); > data[cnt].count = 1; > data_count++; > } > } > > printf("addr: "); > for (unsigned idx = 0; idx < words; idx++) > printf(" %08X", phys + 4 * idx); > printf("\n------"); > for (unsigned idx = 0; idx < words; idx++) > printf("---------"); > putchar('\n'); > > for (unsigned cnt = 0; cnt < data_count; cnt++) { > printf("values"); > for (unsigned idx = 0; idx < words; idx++) > printf(" %8X", data[cnt].v[idx]); > printf(" count %u\n", data[cnt].count); > } > putchar('\n'); > } > > int main(int argc, char **argv) > { > int samples = 100000, mipi = 0, reg_addr = 0, test_read = 0, test_read2 = 0, test_read4 = 0, test_read8 = 0, test_write = 0; > long write_mask = 0xFFFFFFFF; > struct poptOption options[] = { > {"samples", 's', POPT_ARG_INT, &samples, 0, "sample count"}, > {"mipi", 'm', POPT_ARG_INT, &mipi, 0, "MIPI channel"}, > {"address", 'a', POPT_ARG_INT, ®_addr, 0, "ISP register address"}, > {"read-test", 'r', POPT_ARG_NONE, &test_read, 0, "Perform a register read test"}, > {"read-test2", '2', POPT_ARG_NONE, &test_read2, 0, "Perform a 2 * 32-bit register read test"}, > {"read-test4", '4', POPT_ARG_NONE, &test_read4, 0, "Perform a 2 * 64-bit register read test"}, > {"read-test8", '8', POPT_ARG_NONE, &test_read8, 0, "Perform a 2 * 128-bit register read test"}, > {"write-test", 'w', POPT_ARG_NONE, &test_write, 0, "Perform a register write test"}, > {"write-mask", 'M', POPT_ARG_LONG, &write_mask, 0, "Value mask for write test", "MASK"}, > {"alt-read-mode", 'A', POPT_ARG_NONE, &alt_read, 0, "Alternate register read mode"}, > {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "Verbose output"}, > {"debug", 'd', POPT_ARG_NONE, &debug, 0, "Debug output"}, > POPT_AUTOHELP > POPT_TABLEEND > }; > > poptContext context = poptGetContext(NULL, argc, (const char **)argv, options, 0); > int i = poptGetNextOpt(context); > if (i < -1) > errorx("%s: %s\n", poptBadOption(context, POPT_BADOPTION_NOALIAS), poptStrerror(i)); > > if (poptPeekArg(context)) { > poptPrintUsage(context, stderr, 0); > exit(1); > } > poptFreeContext(context); > > if (samples <= 0) > errorx("Invalid number of samples"); > if (mipi != 0 && mipi != 1) > errorx("Invalid MIPI interface index"); > if (test_read + test_read2 + test_read4 + test_read8 + test_write > 1) > errorx("Multiple tests requested"); > if (write_mask < 1 || write_mask > 0xFFFFFFFF) > errorx("Invalid write mask"); > > int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); > if (mem_fd < 0) > error("Error opening /dev/mem"); > > uint32_t phys = mipi ? ISP2_ADDR : ISP1_ADDR; > if (debug) > printf("MIPI_ISP%u registers at 0x%X (size 0x%X)\n", mipi, phys, ISP_SIZE); > void *virt = mmap(0, ISP_SIZE, test_write ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, mem_fd, phys); > if (virt == MAP_FAILED) > error("Mmap failed"); > > if (debug) > printf("Mapped ISP registers at %p\n", virt); > if (test_read || test_read2 || test_read4 || test_read8 || test_write) { > if (reg_addr & 3 || reg_addr < phys || reg_addr >= phys + ISP_SIZE) > errorx("Invalid ISP register address 0x%08X", reg_addr); > virt += reg_addr - phys; > if (debug) > printf("Register 0x%X mapped at %p\n", reg_addr, virt); > if (test_read) > test_reg(samples, reg_addr, virt, 0); > else if (test_read2) > test_reg_ldp(samples, reg_addr, virt, 2); > else if (test_read4) > test_reg_ldp(samples, reg_addr, virt, 4); > else if (test_read8) > test_reg_ldp(samples, reg_addr, virt, 8); > else > test_reg(samples, reg_addr, virt, write_mask); > } else > test_all(samples, phys + MI_OFFSET, virt + MI_OFFSET); > > return 0; > } > > -- > Krzysztof "Chris" Hałasa > > Sieć Badawcza Łukasiewicz > Przemysłowy Instytut Automatyki i Pomiarów PIAP > Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-23 10:19 ` Stefan Klug @ 2025-07-23 12:06 ` Krzysztof Hałasa 2025-07-23 12:09 ` Laurent Pinchart 2025-07-24 8:21 ` Krzysztof Hałasa 1 sibling, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-07-23 12:06 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Stefan, Stefan Klug <stefan.klug@ideasonboard.com> writes: > Just a quick heads up. I ran the tester and so far no unexpected > results. I'll run it from time to time after a reboot to see if I ever > hit that condition. > > How does your device tree look like? Any chance the ISP is clocked for > overdrive but the device is not or something similar? Although I have a > hard time imagining how that would lead to such effects. Interesting. I tested it on two different devices (a Compulab UCM-based camera and a Solidrun Hummingboard Mate) and it's the same on both. I think the first one uses 1600 MHz industrial CPU: (U-boot) CPU: i.MX8MP[8] rev1.1 at 1200 MHz Not sure about the Hummingboard. Both cameras apparently are connected to the second MIPI. Well... maybe if it's only the second ISP, and there is only one camera, then we could reroute the data to the first ISP? The MIPI receiver has a crossbar I think. And the other way around: for a test, one could reroute MIPI0 to ISP1. Will have a look. The DT has the usual stuff (for the second MIPI/ISP): &i2c6 { clock-frequency = <400000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c6>; single-master; status = "okay"; imx462_camera@1a { compatible = "sony,imx462"; reg = <0x1a>; clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>; clock-names = "xclk"; clock-frequency = <37125000>; reset-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>; status = "okay"; port { imx462_mipi_ep: endpoint { data-lanes = <1 2 3 4>; clock-lanes = <0>; link-frequencies = /bits/ 64 <222750000 148500000>; remote-endpoint = <&mipi_csi_1_in>; }; }; }; }; &mipi_csi_1 { status = "okay"; ports { port@0 { reg = <0>; mipi_csi_1_in: endpoint { remote-endpoint = <&imx462_mipi_ep>; data-lanes = <1 2 3 4>; }; }; port@1 { reg = <1>; mipi_csi_1_out: endpoint { remote-endpoint = <&isp_1_in>; }; }; }; }; &isp_1 { status = "okay"; ports { port@1 { isp_1_in: endpoint { bus-type = <MEDIA_BUS_TYPE_PARALLEL>; remote-endpoint = <&mipi_csi_1_out>; }; }; }; }; The CCM registers show basically (p. 229 in i.MX8MP ref manual): 8 MEDIA_ISP mux 7 post 0 SYSTEM_PLL2_DIV2 = 500 MHz 20 MEDIA_AXI mux 1 pre 1 post 0 SYSTEM_PLL2_CLK / 2 = 500 MHz 21 MEDIA_APB mux 2 pre 3 post 0 SYSTEM_PLL1_CLK / 4 = 200 MHz 123 MEDIA_MIPI_PHY1_REF mux 0 pre 0 post 0 24M_REF_CLK = 24 MHz 125 MEDIA_CAM2_PIX mux 2 pre 0 post 0 SYSTEM_PLL2_DIV4 = 250 MHz The first 3 are at the max values, MEDIA_MIPI_PHY1_REF max is 125 MHz, MEDIA_CAM2_PIX max is 266 MHz. Maybe I should try changing these clocks, but not sure how do I do that (any change causes rkisp1 driver loading to fail). Will look at it. BTW the double read and double write in NXP driver (isp-vvcam) were introduced by (in their repo): Author: hexing <Xing.He@verisilicon.com> 2022-08-05 10:19:49 Committer: Robby Cai <robby.cai@nxp.com> 2022-08-08 04:50:48 M865SW-1031: the second isp port jump frames Reason:mi read or write reg occasionally it does not take effect WorkAround:read or write twice of mi reg ---------------------------- vvcam/isp/isp_ioctl.c ---------------------------- index 60741bd..e0d3048 100644 @@ -118,5 +118,8 @@ void isp_write_reg(struct isp_ic_dev *dev, u32 offset, u32 val) if (offset >= ISP_REG_SIZE) return; - __raw_writel(val, dev->base + offset); + writel(val, dev->base + offset); + if ((offset >= REG_ADDR(mi_mp_y_base_ad_init)) + && (offset <= REG_ADDR(mi_mp_y_pic_size))) + writel(val, dev->base + offset); // isp_info("%s addr 0x%08x val 0x%08x\n", __func__, offset, val); } @@ -128,5 +131,8 @@ u32 isp_read_reg(struct isp_ic_dev *dev, u32 offset) if (offset >= ISP_REG_SIZE) return 0; - val = __raw_readl(dev->base + offset); + val = readl(dev->base + offset); + if ((offset >= REG_ADDR(mi_mp_y_base_ad_init)) + && (offset <= REG_ADDR(mi_mp_y_pic_size))) + val = readl(dev->base + offset); // isp_info("%s addr 0x%08x val 0x%08x\n", __func__, offset, val); return val; -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-23 12:06 ` Krzysztof Hałasa @ 2025-07-23 12:09 ` Laurent Pinchart 2025-07-24 4:20 ` Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Laurent Pinchart @ 2025-07-23 12:09 UTC (permalink / raw) To: Krzysztof Hałasa Cc: Stefan Klug, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel On Wed, Jul 23, 2025 at 02:06:43PM +0200, Krzysztof Hałasa wrote: > Stefan Klug writes: > > > Just a quick heads up. I ran the tester and so far no unexpected > > results. I'll run it from time to time after a reboot to see if I ever > > hit that condition. > > > > How does your device tree look like? Any chance the ISP is clocked for > > overdrive but the device is not or something similar? Although I have a > > hard time imagining how that would lead to such effects. > > Interesting. > I tested it on two different devices (a Compulab UCM-based camera and > a Solidrun Hummingboard Mate) and it's the same on both. I think the > first one uses 1600 MHz industrial CPU: > > (U-boot) CPU: i.MX8MP[8] rev1.1 at 1200 MHz > > Not sure about the Hummingboard. > > Both cameras apparently are connected to the second MIPI. > > Well... maybe if it's only the second ISP, and there is only one camera, > then we could reroute the data to the first ISP? The MIPI receiver has > a crossbar I think. There's a crossbar switch in the ISI, but the connections from CSI-2 receivers to ISPs are fixed. Have you tried reporting the issue to NXP ? > And the other way around: for a test, one could reroute MIPI0 to ISP1. > Will have a look. > > The DT has the usual stuff (for the second MIPI/ISP): > > &i2c6 { > clock-frequency = <400000>; > pinctrl-names = "default"; > pinctrl-0 = <&pinctrl_i2c6>; > single-master; > status = "okay"; > > imx462_camera@1a { > compatible = "sony,imx462"; > reg = <0x1a>; > clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>; > clock-names = "xclk"; > clock-frequency = <37125000>; > reset-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>; > status = "okay"; > > port { > imx462_mipi_ep: endpoint { > data-lanes = <1 2 3 4>; > clock-lanes = <0>; > link-frequencies = /bits/ 64 <222750000 148500000>; > remote-endpoint = <&mipi_csi_1_in>; > }; > }; > }; > > }; > > &mipi_csi_1 { > status = "okay"; > > ports { > port@0 { > reg = <0>; > mipi_csi_1_in: endpoint { > remote-endpoint = <&imx462_mipi_ep>; > data-lanes = <1 2 3 4>; > }; > }; > > port@1 { > reg = <1>; > mipi_csi_1_out: endpoint { > remote-endpoint = <&isp_1_in>; > }; > }; > }; > }; > > &isp_1 { > status = "okay"; > > ports { > port@1 { > isp_1_in: endpoint { > bus-type = <MEDIA_BUS_TYPE_PARALLEL>; > remote-endpoint = <&mipi_csi_1_out>; > }; > }; > }; > }; > > The CCM registers show basically (p. 229 in i.MX8MP ref manual): > 8 MEDIA_ISP mux 7 post 0 SYSTEM_PLL2_DIV2 = 500 MHz > 20 MEDIA_AXI mux 1 pre 1 post 0 SYSTEM_PLL2_CLK / 2 = 500 MHz > 21 MEDIA_APB mux 2 pre 3 post 0 SYSTEM_PLL1_CLK / 4 = 200 MHz > 123 MEDIA_MIPI_PHY1_REF mux 0 pre 0 post 0 24M_REF_CLK = 24 MHz > 125 MEDIA_CAM2_PIX mux 2 pre 0 post 0 SYSTEM_PLL2_DIV4 = 250 MHz > > The first 3 are at the max values, MEDIA_MIPI_PHY1_REF max is 125 MHz, > MEDIA_CAM2_PIX max is 266 MHz. Maybe I should try changing these clocks, > but not sure how do I do that (any change causes rkisp1 driver loading > to fail). Will look at it. > > BTW the double read and double write in NXP driver (isp-vvcam) were > introduced by (in their repo): > > Author: hexing <Xing.He@verisilicon.com> 2022-08-05 10:19:49 > Committer: Robby Cai <robby.cai@nxp.com> 2022-08-08 04:50:48 > > M865SW-1031: the second isp port jump frames > > Reason:mi read or write reg occasionally it does not take effect > > WorkAround:read or write twice of mi reg > > ---------------------------- vvcam/isp/isp_ioctl.c ---------------------------- > index 60741bd..e0d3048 100644 > @@ -118,5 +118,8 @@ void isp_write_reg(struct isp_ic_dev *dev, u32 offset, u32 val) > if (offset >= ISP_REG_SIZE) > return; > - __raw_writel(val, dev->base + offset); > + writel(val, dev->base + offset); > + if ((offset >= REG_ADDR(mi_mp_y_base_ad_init)) > + && (offset <= REG_ADDR(mi_mp_y_pic_size))) > + writel(val, dev->base + offset); > // isp_info("%s addr 0x%08x val 0x%08x\n", __func__, offset, val); > } > @@ -128,5 +131,8 @@ u32 isp_read_reg(struct isp_ic_dev *dev, u32 offset) > if (offset >= ISP_REG_SIZE) > return 0; > - val = __raw_readl(dev->base + offset); > + val = readl(dev->base + offset); > + if ((offset >= REG_ADDR(mi_mp_y_base_ad_init)) > + && (offset <= REG_ADDR(mi_mp_y_pic_size))) > + val = readl(dev->base + offset); > // isp_info("%s addr 0x%08x val 0x%08x\n", __func__, offset, val); > return val; > -- Regards, Laurent Pinchart ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-23 12:09 ` Laurent Pinchart @ 2025-07-24 4:20 ` Krzysztof Hałasa 0 siblings, 0 replies; 23+ messages in thread From: Krzysztof Hałasa @ 2025-07-24 4:20 UTC (permalink / raw) To: Laurent Pinchart Cc: Stefan Klug, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Laurent Pinchart <laurent.pinchart@ideasonboard.com> writes: > There's a crossbar switch in the ISI, but the connections from CSI-2 > receivers to ISPs are fixed. > > Have you tried reporting the issue to NXP ? No, they know about it, there is that workaround code (working most of the time) in their driver. For now, I want to better know the problem. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-23 10:19 ` Stefan Klug 2025-07-23 12:06 ` Krzysztof Hałasa @ 2025-07-24 8:21 ` Krzysztof Hałasa 2025-07-25 11:09 ` Stefan Klug 1 sibling, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-07-24 8:21 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Stefan, > Just a quick heads up. I ran the tester and so far no unexpected > results. I'll run it from time to time after a reboot to see if I ever > hit that condition. Is your camera(s) connected to the first CSI? I cannot reproduce the problem on csi0, it seems only csi1 is affected. It would be consistent with NXP's workaround commit text. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-24 8:21 ` Krzysztof Hałasa @ 2025-07-25 11:09 ` Stefan Klug 2025-07-30 10:30 ` Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Stefan Klug @ 2025-07-25 11:09 UTC (permalink / raw) To: Krzysztof Hałasa Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Krzysztof, Quoting Krzysztof Hałasa (2025-07-24 10:21:45) > Hi Stefan, > > > Just a quick heads up. I ran the tester and so far no unexpected > > results. I'll run it from time to time after a reboot to see if I ever > > hit that condition. > > Is your camera(s) connected to the first CSI? > I cannot reproduce the problem on csi0, it seems only csi1 is > affected. It would be consistent with NXP's workaround commit text. Yes in that case it is connected to csi0. I sometimes have cases where one is connected to csi1. I'll rerun the test when I use tha device again. Best regards, Stefan > -- > Krzysztof "Chris" Hałasa > > Sieć Badawcza Łukasiewicz > Przemysłowy Instytut Automatyki i Pomiarów PIAP > Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-25 11:09 ` Stefan Klug @ 2025-07-30 10:30 ` Krzysztof Hałasa 2025-08-01 12:19 ` Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-07-30 10:30 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi, still investigating the problem, but I can't see a simple way to work around it. It happens with the second ISP only, so the single camera setups (using isp0) are immune. The ISP MI (memory interface) register read operations are not a problem, the workaround uses LDP instructions (for registers located at addresses not on 32-byte boundary) or read multiple times and return the most frequent result (fortunately the MI reads have no side effects). The writes are a problem, though. Especially writing to RKISP1_CIF_MI_ICR register (address 0x32E21504) can (and does) corrupt other MI registers, like RKISP1_CIF_MI_CTRL (0x32E21400): Camera apps just started: 32E21400: 7A2001 20 3C180000 1FA400 32E21410: 0 0 0 3C200000 32E21420: FD200 0 0 0 32E21470: 0 10001 3C0C0000 1FA400 32E21480: 0 0 3C140000 FD200 32E214F0: 0 0 1 3C 32E21540: 0 0 0 2000000 32E21550: 780 0 0 1 32E21560: 0 780 438 1FA400 32E21570: 0 1E1E00EF 1E2200E0 0 Video stream halted: 32E21400: 1000007 20 3C240000 1FA400 32E21410: 0 0 0 3C2C0000 32E21420: FD200 0 0 0 32E21470: 0 0 3C0C0000 1FA400 32E21480: 0 0 3C140000 FD200 32E21490: 7E900 0 0 0 32E214F0: 0 0 1 7C 32E21500: 0 0 0 7 32E21540: 0 0 0 2000000 32E21550: 780 0 0 1 32E21560: 0 780 438 1FA400 32E21570: 0 1E060000 1E0A0000 77 Note that e.g. the 0x32E21400 register, MI_CTRL, has changed value from the valid one (0x7A2001) to 0x1000007, which probably originated in MI_MIS (and was to be written to MI_ICR). It appears the problems are not constant, they manifest themselves in maybe 10% of system boots. Once a system boots, the problems are either present (and don't go away) or not. It appears the ISP uses 3 clocks (+2 used by CSI receiver) - dump of their CCM registers (Clock Control Module, IMX8MPRM page 227 and so on): MEDIA_ISP EN mux 7 post 0 SYSTEM_PLL2_DIV2 = 500 MHz MEDIA_AXI EN mux 1 pre 1 post 0 SYSTEM_PLL2_CLK / 2 = 500 MHz MEDIA_APB EN mux 2 pre 3 post 0 SYSTEM_PLL1_CLK / 4 = 200 MHz MEDIA_MIPI_PHY1_REF EN mux 0 pre 0 post 0 24M_REF_CLK = 24 MHz MEDIA_CAM2_PIX EN mux 2 pre 0 post 0 SYSTEM_PLL2_DIV4 = 250 MHz Now, it appears that in certain cases changing the MEDIA_APB (MEDIA_APB_CLOCK_ROOT) "fixes" the problem (for current boot only), or make it less (or more) visible. I'm changing the POST divider in CCM_POST_ROOT21_SET register directly (the MEDIA_APB frequency is then 100 MHz): 0x30388A80 MEDIA_APB EN mux 2 pre 3 post 0 SYSTEM_PLL1_CLK / 4 = 200 MHz # devmem write32 0x30388AA4 1 0x30388A80 MEDIA_APB EN mux 2 pre 3 post 1 SYSTEM_PLL1_CLK / 8 = 100 MHz Examples - bad read counts from register 0x32E21400 (mi_ctrl) per 10 millions of read operations: POST=0 (default) with POST=1 945 1 1448 2361 1088 2419 1842 2451 1333 0 67 5 2033 0 1 8 13 16 2 4 7 0 1 0 539 0 2605 0 1 0 1723 1 I'm starting to run out of ideas. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-07-30 10:30 ` Krzysztof Hałasa @ 2025-08-01 12:19 ` Krzysztof Hałasa 2025-08-05 10:57 ` Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-08-01 12:19 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi, reporting from the field :-) At this point I've something like the following: - run camera apps in background (so the clocks etc. are up) - while (analyze_mi -m1 -s10000000 -r -a 0x32E21400 shows errors) { change_clock 0x30388A80 3 3 # 50 MHz change_clock 0x30388A80 0 3 # 200 MHz - the original settings } It all takes a couple of seconds. I guess I made some minor changes to analyze_mi.c, can post it if needed. where change_clock writes directly do the CCM registers (MEDIA_APB_ROOT_CLK) and sets "PRE" and "POST" dividers (3 and 3 = 800 MHz / (3 + 1) / (3 + 1) = 50 Mhz and the same for 200 Mhz. It appears to work. I'm getting (Connection closed = reboot): MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 9,999,999 Register 0x32E21400 mi_ctrl value 0 count 1 MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 16 = 50 MHz MEDIA_APB EN mux 2 pre 0 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 9,999,998 Register 0x32E21400 mi_ctrl value 0 count 1 Register 0x32E21400 mi_ctrl value 0x00010001 count 1 MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 16 = 50 MHz MEDIA_APB EN mux 2 pre 0 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 9,999,988 Register 0x32E21400 mi_ctrl value 0 count 12 MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 16 = 50 MHz MEDIA_APB EN mux 2 pre 0 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 9,999,781 Register 0x32E21400 mi_ctrl value 0 count 206 Register 0x32E21400 mi_ctrl value 0x3C380000 count 3 Register 0x32E21400 mi_ctrl value 0x000FD200 count 5 Register 0x32E21400 mi_ctrl value 0x3C440000 count 3 Register 0x32E21400 mi_ctrl value 0x3C140000 count 2 MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 16 = 50 MHz MEDIA_APB EN mux 2 pre 0 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 9,999,999 Register 0x32E21400 mi_ctrl value 0 count 1 MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 16 = 50 MHz MEDIA_APB EN mux 2 pre 0 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 9,999,752 Register 0x32E21400 mi_ctrl value 0 count 237 Register 0x32E21400 mi_ctrl value 0x3C140000 count 2 Register 0x32E21400 mi_ctrl value 0x000FD200 count 4 Register 0x32E21400 mi_ctrl value 0x3C380000 count 1 Register 0x32E21400 mi_ctrl value 0x3C440000 count 2 Register 0x32E21400 mi_ctrl value 0x3C2C0000 count 2 MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 16 = 50 MHz MEDIA_APB EN mux 2 pre 0 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition Connection to 10.0.9.123 closed by remote host. MEDIA_APB EN mux 2 pre 3 SYSTEM_PLL1_CLK / 4 = 200 MHz Register 0x32E21400 mi_ctrl value 0x007A2001 count 10,000,000 Found possibly stable condition -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-08-01 12:19 ` Krzysztof Hałasa @ 2025-08-05 10:57 ` Krzysztof Hałasa 2025-08-05 11:13 ` Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-08-05 10:57 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi, just in case someone finds it helpful: SError Interrupt on CPU3, code 0x00000000bf000002 -- SError CPU: 3 UID: 0 PID: 40083 Comm: devmem Not tainted 6.15+ PREEMPT pstate: 60000000 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) Kernel panic - not syncing: Asynchronous SError Interrupt What did i do? Wrote 32-bit value to an ISP register (0x32E22E2E4C = a histogram-64 register) while repeatedly reading other ISP registers (in userspace). The video stream was active. This ISP apparently has its own problems. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption 2025-08-05 10:57 ` Krzysztof Hałasa @ 2025-08-05 11:13 ` Krzysztof Hałasa 2025-08-12 5:54 ` FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved Krzysztof Hałasa 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-08-05 11:13 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Krzysztof Hałasa <khalasa@piap.pl> writes: > SError Interrupt on CPU3, code 0x00000000bf000002 -- SError Could have been PEBKAC, though. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-05 11:13 ` Krzysztof Hałasa @ 2025-08-12 5:54 ` Krzysztof Hałasa 2025-08-12 10:32 ` Laurent Pinchart 0 siblings, 1 reply; 23+ messages in thread From: Krzysztof Hałasa @ 2025-08-12 5:54 UTC (permalink / raw) To: Stefan Klug Cc: Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Stefan et al, BTW I've added Lucas Stach and Shawn Guo to "Cc" list. The problem is the CPU core power supply voltage :-) - while the reference manual specifies the max ISP and MEDIA clocks at 500 MHz, the datasheets show this requires the "overdrive" mode = increased CPU power supply voltage. In "normal" mode the ISPs are limited to 400 MHz (there are other limits, too). - I've tried lowering the clock rate after booting the systems (with a CCM register write), but it didn't fix the problem. I guess some reset logic is affected here, and the (lower) clock rate must be set right from the start, in the DT. - anyway, lowering the frequencies of ISP and MEDIA root clocks fixes the ISP2 MI corruption. I'm currently investigating PMIC settings (both my Compulab and SolidRun modules use PCA9450C PMICs), so perhaps I'll be able to use the higher 500 MHz clocks. It doesn't matter much, though. - the question is if we should lower the clocks in the main imx8mp.dtsi DT file, or the overdrive mode should stay there, and the changes should be made to the individual board files, or maybe the U-Boot configs (PMIC output voltages) should be changed etc. -- Krzysztof "Chris" HaBasa Sieć Badawcza Aukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-12 5:54 ` FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved Krzysztof Hałasa @ 2025-08-12 10:32 ` Laurent Pinchart 2025-08-12 12:28 ` Krzysztof Hałasa 2025-08-12 15:02 ` Stefan Klug 0 siblings, 2 replies; 23+ messages in thread From: Laurent Pinchart @ 2025-08-12 10:32 UTC (permalink / raw) To: Krzysztof Hałasa Cc: Stefan Klug, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Krzysztof, On Tue, Aug 12, 2025 at 07:54:46AM +0200, Krzysztof Hałasa wrote: > Hi Stefan et al, > > BTW I've added Lucas Stach and Shawn Guo to "Cc" list. > > The problem is the CPU core power supply voltage :-) Ah, the dreadful overdrive mode. > - while the reference manual specifies the max ISP and MEDIA clocks at > 500 MHz, the datasheets show this requires the "overdrive" mode = > increased CPU power supply voltage. In "normal" mode the ISPs are > limited to 400 MHz (there are other limits, too). > > - I've tried lowering the clock rate after booting the systems (with > a CCM register write), but it didn't fix the problem. I guess some > reset logic is affected here, and the (lower) clock rate must be set > right from the start, in the DT. That's interesting. I wouldn't have expected that. > - anyway, lowering the frequencies of ISP and MEDIA root clocks fixes > the ISP2 MI corruption. I'm currently investigating PMIC settings > (both my Compulab and SolidRun modules use PCA9450C PMICs), so perhaps > I'll be able to use the higher 500 MHz clocks. It doesn't matter much, > though. > > - the question is if we should lower the clocks in the main imx8mp.dtsi > DT file, or the overdrive mode should stay there, and the changes > should be made to the individual board files, or maybe the U-Boot > configs (PMIC output voltages) should be changed etc. I think it would make sense to lower the default clock frequencies, and provide an overlay to enable overdrive mode. It's also interesting that the issue only affected the second ISP, as the first one should also be limited to 400 MHz in normal mode. -- Regards, Laurent Pinchart ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-12 10:32 ` Laurent Pinchart @ 2025-08-12 12:28 ` Krzysztof Hałasa 2025-08-12 15:02 ` Stefan Klug 1 sibling, 0 replies; 23+ messages in thread From: Krzysztof Hałasa @ 2025-08-12 12:28 UTC (permalink / raw) To: Laurent Pinchart Cc: Stefan Klug, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Laurent, >> - anyway, lowering the frequencies of ISP and MEDIA root clocks fixes >> the ISP2 MI corruption. I'm currently investigating PMIC settings >> (both my Compulab and SolidRun modules use PCA9450C PMICs), so perhaps >> I'll be able to use the higher 500 MHz clocks. It doesn't matter much, >> though. Well, apparently my Compulab UCM module provides overdrive power. At least the PMIC driver says so (in /sys pseudofiles). I guess SolidRun won't be different. 1. At least two different designs are affected (Compulab and SolidRun), 2. the PCA9450C is THE i.MX8MP PMIC, most probably connected to the CPU exactly as in the datasheet (at least WRT VDD_SOC power lines), 3. NXP people experienced the problem on their (which ones?) boards, too Well... They specify (IMX8MPIEC = the datasheet): "Two instances of 4-lane MIPI CSI interface and HDR ISP • For single Camera, MIPI CSI 1 can support up to 400/500 MHz pixel clock in the Nominal/Overdrive mode. • For single Camera, MIPI CSI 2 can support up to 277 MHz pixel clock. • For dual Camera, both MIPI CSI can support up to 266 MHz pixel clock. • 2x ISP supporting 375 Mpixel/s aggregate performance and up to 3-exposure HDR processing. •When one camera is used, support up to 12MP@30fps or 4kp45 •When two cameras are used, each supports up to 1080p80" So, while ISP clock is not exactly pixel clock, perhaps they mean that the ISP clock is to be limited as well. ISP2 at 400 MHz (single camera) works for me (with a 1080p60 12-bit IMX462 sensor), but I haven't tested in extreme conditions etc. > I think it would make sense to lower the default clock frequencies, and > provide an overlay to enable overdrive mode. > > It's also interesting that the issue only affected the second ISP, as > the first one should also be limited to 400 MHz in normal mode. Right. Maybe it's not the overdrive mode after all. I will do more tests soon. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-12 10:32 ` Laurent Pinchart 2025-08-12 12:28 ` Krzysztof Hałasa @ 2025-08-12 15:02 ` Stefan Klug 2025-08-12 18:22 ` Adam Ford 1 sibling, 1 reply; 23+ messages in thread From: Stefan Klug @ 2025-08-12 15:02 UTC (permalink / raw) To: Krzysztof Hałasa, Laurent Pinchart Cc: Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Krzysztof, hi Laurent, Quoting Laurent Pinchart (2025-08-12 12:32:43) > Hi Krzysztof, > > On Tue, Aug 12, 2025 at 07:54:46AM +0200, Krzysztof Hałasa wrote: > > Hi Stefan et al, > > > > BTW I've added Lucas Stach and Shawn Guo to "Cc" list. > > > > The problem is the CPU core power supply voltage :-) > > Ah, the dreadful overdrive mode. > > > - while the reference manual specifies the max ISP and MEDIA clocks at > > 500 MHz, the datasheets show this requires the "overdrive" mode = > > increased CPU power supply voltage. In "normal" mode the ISPs are > > limited to 400 MHz (there are other limits, too). > > > > - I've tried lowering the clock rate after booting the systems (with > > a CCM register write), but it didn't fix the problem. I guess some > > reset logic is affected here, and the (lower) clock rate must be set > > right from the start, in the DT. > > That's interesting. I wouldn't have expected that. > > > - anyway, lowering the frequencies of ISP and MEDIA root clocks fixes > > the ISP2 MI corruption. I'm currently investigating PMIC settings > > (both my Compulab and SolidRun modules use PCA9450C PMICs), so perhaps > > I'll be able to use the higher 500 MHz clocks. It doesn't matter much, > > though. > > > > - the question is if we should lower the clocks in the main imx8mp.dtsi > > DT file, or the overdrive mode should stay there, and the changes > > should be made to the individual board files, or maybe the U-Boot > > configs (PMIC output voltages) should be changed etc. > > I think it would make sense to lower the default clock frequencies, and > provide an overlay to enable overdrive mode. > > It's also interesting that the issue only affected the second ISP, as > the first one should also be limited to 400 MHz in normal mode. I support that. As a side note, there is already imx8mp-nominal.dtsi which is only used by one board. That dtsi also uses the fsl,operating-mode property which enables additional clock checks. So I ask myself if the default imx8mp.dtsi should specify overdrive mode, or if we should add a imx8mp-overdrive.dtsi (then we should possibly rename them to imx8mp-mode-xxx.dtsi so that they sit side by side) to make it easier to create overlays for both cases. Best regards, Stefan > > -- > Regards, > > Laurent Pinchart ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-12 15:02 ` Stefan Klug @ 2025-08-12 18:22 ` Adam Ford 2025-08-13 5:10 ` Krzysztof Hałasa 2025-09-02 9:54 ` Krzysztof Hałasa 0 siblings, 2 replies; 23+ messages in thread From: Adam Ford @ 2025-08-12 18:22 UTC (permalink / raw) To: Stefan Klug Cc: Krzysztof Hałasa, Laurent Pinchart, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel On Tue, Aug 12, 2025 at 10:07 AM Stefan Klug <stefan.klug@ideasonboard.com> wrote: > > Hi Krzysztof, hi Laurent, > > Quoting Laurent Pinchart (2025-08-12 12:32:43) > > Hi Krzysztof, > > > > On Tue, Aug 12, 2025 at 07:54:46AM +0200, Krzysztof Hałasa wrote: > > > Hi Stefan et al, > > > > > > BTW I've added Lucas Stach and Shawn Guo to "Cc" list. > > > > > > The problem is the CPU core power supply voltage :-) > > > > Ah, the dreadful overdrive mode. > > > > > - while the reference manual specifies the max ISP and MEDIA clocks at > > > 500 MHz, the datasheets show this requires the "overdrive" mode = > > > increased CPU power supply voltage. In "normal" mode the ISPs are > > > limited to 400 MHz (there are other limits, too). > > > > > > - I've tried lowering the clock rate after booting the systems (with > > > a CCM register write), but it didn't fix the problem. I guess some > > > reset logic is affected here, and the (lower) clock rate must be set > > > right from the start, in the DT. > > > > That's interesting. I wouldn't have expected that. > > > > > - anyway, lowering the frequencies of ISP and MEDIA root clocks fixes > > > the ISP2 MI corruption. I'm currently investigating PMIC settings > > > (both my Compulab and SolidRun modules use PCA9450C PMICs), so perhaps > > > I'll be able to use the higher 500 MHz clocks. It doesn't matter much, > > > though. I was reading through the data sheet (not the reference manual), and it lists a few limitations for the clocks: For single Camera, MIPI CSI 1 can support up to 400/500 MHz pixel clock in the Nominal/Overdrive mode. For single Camera, MIPI CSI 2 can support up to 277 MHz pixel clock. For dual Camera, both MIPI CSI can support up to 266 MHz pixel clock. If you're running dual cameras, it sounds like you're capped at 266 MHz regardless of whether or not you're in overdrive or nominal. > > > > > > - the question is if we should lower the clocks in the main imx8mp.dtsi > > > DT file, or the overdrive mode should stay there, and the changes > > > should be made to the individual board files, or maybe the U-Boot > > > configs (PMIC output voltages) should be changed etc. > > > > I think it would make sense to lower the default clock frequencies, and > > provide an overlay to enable overdrive mode. > > > > It's also interesting that the issue only affected the second ISP, as > > the first one should also be limited to 400 MHz in normal mode. > > I support that. As a side note, there is already imx8mp-nominal.dtsi > which is only used by one board. That dtsi also uses the > fsl,operating-mode property which enables additional clock checks. So I > ask myself if the default imx8mp.dtsi should specify overdrive mode, or > if we should add a imx8mp-overdrive.dtsi (then we should possibly rename > them to imx8mp-mode-xxx.dtsi so that they sit side by side) to make it > easier to create overlays for both cases. My understanding is that the imx8mp.dtsi is pre-configured for overdrive mode, so if you need to run the ISP in nominal, the clock updates should go into imx8mp-nominial.dtsi. adam > > Best regards, > Stefan > > > > > -- > > Regards, > > > > Laurent Pinchart > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-12 18:22 ` Adam Ford @ 2025-08-13 5:10 ` Krzysztof Hałasa 2025-09-02 9:54 ` Krzysztof Hałasa 1 sibling, 0 replies; 23+ messages in thread From: Krzysztof Hałasa @ 2025-08-13 5:10 UTC (permalink / raw) To: Adam Ford Cc: Stefan Klug, Laurent Pinchart, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Adam, Adam Ford <aford173@gmail.com> writes: > I was reading through the data sheet (not the reference manual), and > it lists a few limitations for the clocks: > > For single Camera, MIPI CSI 1 can support up to 400/500 MHz pixel > clock in the Nominal/Overdrive mode. > For single Camera, MIPI CSI 2 can support up to 277 MHz pixel clock. > For dual Camera, both MIPI CSI can support up to 266 MHz pixel clock. > > If you're running dual cameras, it sounds like you're capped at 266 > MHz regardless of whether or not you're in overdrive or nominal. Right. But these are pixel clocks, not ISP clock. At least theoretically entirely different stuff. For example, I'm using (at most) two IMX290/462 1920x1080p60 sensors in 12-bit (Bayer) mode, resulting in: - 148.5 MHz pixel clocks - 1782 Mb/s total bandwidth (for each sensor - 12 bits) - 445.5 Mb/s per lane (each sensor uses 4 MIPI lanes) - 222.75 MHz MIPI clocks (MIPI uses DDR, the double data rate) So I'm well within specs. Though I don't know if the people writing the datasheets did really mean what they wrote, or maybe they meant ISP core clocks as well. Why? Because ISP blocks (and all such logic blocks) require a certain number of their core clocks for a signal rate (in this case, a pixel rate). And maybe what they specified in the datasheets was a calculation of this (unpublished?) required pixel/ISP clock ratio and the max ISP clock rate (specified incorrectly at 400/500 MHz). Anyway, this is only a possibility. Another one is that ISP2 simply cannot reliably run at 500 MHz, for some "analog" reason, design bug etc. > My understanding is that the imx8mp.dtsi is pre-configured for > overdrive mode, so if you need to run the ISP in nominal, the clock > updates should go into imx8mp-nominial.dtsi. It seems my modules are setup for overdrive mode. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-08-12 18:22 ` Adam Ford 2025-08-13 5:10 ` Krzysztof Hałasa @ 2025-09-02 9:54 ` Krzysztof Hałasa 2025-09-02 11:23 ` Alexander Stein 2025-09-03 0:56 ` Paul Elder 1 sibling, 2 replies; 23+ messages in thread From: Krzysztof Hałasa @ 2025-09-02 9:54 UTC (permalink / raw) To: Adam Ford Cc: Stefan Klug, Laurent Pinchart, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi, summary: I've done a few additional tests and it seems the MEDIA_AXI clock is the problem. Reducing it to 400 MHz while still running MEDIA_ISP at 500 MHz produces no errors. MEDIA_ISP at 400 MHz and MEDIA_AXI at 500 MHz produces errors, though (register address errors while reading and writing from/to ISP MI (memory interface) registers, only on the secondary ISP (isp1), and generally only while streaming data from the ISP). What is driven by MEDIA_AXI clock root? MEDIAMIX: ISI, LCDIF, ISP, DWE. According to both datasheets (industrial and commercial), MEDIA_AXI is limited to 400 MHz in normal mode and 500 MHz in overdrive mode. All my hardware is setup for overdrive mode, though (two manufacturers, both using the same PMIC setup). Since no hardware in the official Linux kernel tree (DT) uses the second ISP... Should we just add a warning to the imx8mp.dtsi and be done with it? Out of tree hardware using isp1 (csi1) obviously exists. -- Krzysztof "Chris" Hałasa Sieć Badawcza Łukasiewicz Przemysłowy Instytut Automatyki i Pomiarów PIAP Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-09-02 9:54 ` Krzysztof Hałasa @ 2025-09-02 11:23 ` Alexander Stein 2025-09-03 0:56 ` Paul Elder 1 sibling, 0 replies; 23+ messages in thread From: Alexander Stein @ 2025-09-02 11:23 UTC (permalink / raw) To: Adam Ford, Krzysztof Hałasa Cc: Stefan Klug, Laurent Pinchart, Dafna Hirschfeld, Heiko Stuebner, Paul Elder, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Am Dienstag, 2. September 2025, 11:54:27 CEST schrieb Krzysztof Hałasa: > Hi, > > summary: > > I've done a few additional tests and it seems the MEDIA_AXI clock is the > problem. Reducing it to 400 MHz while still running MEDIA_ISP at 500 MHz > produces no errors. > MEDIA_ISP at 400 MHz and MEDIA_AXI at 500 MHz produces errors, though > (register address errors while reading and writing from/to ISP MI > (memory interface) registers, only on the secondary ISP (isp1), and > generally only while streaming data from the ISP). > > What is driven by MEDIA_AXI clock root? MEDIAMIX: ISI, LCDIF, ISP, DWE. > > According to both datasheets (industrial and commercial), MEDIA_AXI > is limited to 400 MHz in normal mode and 500 MHz in overdrive mode. > All my hardware is setup for overdrive mode, though (two manufacturers, > both using the same PMIC setup). > > Since no hardware in the official Linux kernel tree (DT) uses the second > ISP... Should we just add a warning to the imx8mp.dtsi and be done with > it? > Out of tree hardware using isp1 (csi1) obviously exists. There is also [1] which addresses normal mode and clock limits. I don't know if there is any progress. Best regards Alexander [1] https://lore.kernel.org/all/20250218-imx8m-clk-v4-2-b7697dc2dcd0@pengutronix.de/ -- TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany Amtsgericht München, HRB 105018 Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider http://www.tq-group.com/ ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved 2025-09-02 9:54 ` Krzysztof Hałasa 2025-09-02 11:23 ` Alexander Stein @ 2025-09-03 0:56 ` Paul Elder 1 sibling, 0 replies; 23+ messages in thread From: Paul Elder @ 2025-09-03 0:56 UTC (permalink / raw) To: Adam Ford, Krzysztof Hałasa Cc: Stefan Klug, Laurent Pinchart, Dafna Hirschfeld, Heiko Stuebner, Jacopo Mondi, Ondrej Jirman, linux-media, linux-rockchip, linux-arm-kernel, linux-kernel Hi Krzysztof, Quoting Krzysztof Hałasa (2025-09-02 18:54:27) > Hi, > > summary: > > I've done a few additional tests and it seems the MEDIA_AXI clock is the > problem. Reducing it to 400 MHz while still running MEDIA_ISP at 500 MHz > produces no errors. > MEDIA_ISP at 400 MHz and MEDIA_AXI at 500 MHz produces errors, though > (register address errors while reading and writing from/to ISP MI > (memory interface) registers, only on the secondary ISP (isp1), and > generally only while streaming data from the ISP). > > What is driven by MEDIA_AXI clock root? MEDIAMIX: ISI, LCDIF, ISP, DWE. > > According to both datasheets (industrial and commercial), MEDIA_AXI > is limited to 400 MHz in normal mode and 500 MHz in overdrive mode. > All my hardware is setup for overdrive mode, though (two manufacturers, > both using the same PMIC setup). Thanks for the investigation! > > Since no hardware in the official Linux kernel tree (DT) uses the second > ISP... Should we just add a warning to the imx8mp.dtsi and be done with > it? afaiu we can't yet use the ISI and ISP simultaneously, so the ISI is enabled by default and you need overlays to enable the ISP, so technically nothing upstream uses either ISP. To my knowledge, there is hardware where you can use both though, such as the Debix SOM A (imx8mp-debix-som-a-bmb-08.dts) [0]. Paul [0] https://debix.io/hardware/debix-som-a-io-board.html > Out of tree hardware using isp1 (csi1) obviously exists. > -- > Krzysztof "Chris" Hałasa > > Sieć Badawcza Łukasiewicz > Przemysłowy Instytut Automatyki i Pomiarów PIAP > Al. Jerozolimskie 202, 02-486 Warszawa ^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2025-09-03 0:59 UTC | newest] Thread overview: 23+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-07-17 9:00 FYI: i.MX8MP ISP (RKISP1) MI registers corruption Krzysztof Hałasa 2025-07-17 13:03 ` Krzysztof Hałasa 2025-07-21 8:46 ` Stefan Klug 2025-07-21 13:16 ` Krzysztof Hałasa 2025-07-23 10:19 ` Stefan Klug 2025-07-23 12:06 ` Krzysztof Hałasa 2025-07-23 12:09 ` Laurent Pinchart 2025-07-24 4:20 ` Krzysztof Hałasa 2025-07-24 8:21 ` Krzysztof Hałasa 2025-07-25 11:09 ` Stefan Klug 2025-07-30 10:30 ` Krzysztof Hałasa 2025-08-01 12:19 ` Krzysztof Hałasa 2025-08-05 10:57 ` Krzysztof Hałasa 2025-08-05 11:13 ` Krzysztof Hałasa 2025-08-12 5:54 ` FYI: i.MX8MP ISP (RKISP1) MI registers corruption: resolved Krzysztof Hałasa 2025-08-12 10:32 ` Laurent Pinchart 2025-08-12 12:28 ` Krzysztof Hałasa 2025-08-12 15:02 ` Stefan Klug 2025-08-12 18:22 ` Adam Ford 2025-08-13 5:10 ` Krzysztof Hałasa 2025-09-02 9:54 ` Krzysztof Hałasa 2025-09-02 11:23 ` Alexander Stein 2025-09-03 0:56 ` Paul Elder
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox