lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <m31pq9y98z.fsf@t19.piap.pl>
Date: Mon, 21 Jul 2025 15:16:28 +0200
From: Krzysztof Hałasa <khalasa@...p.pl>
To: Stefan Klug <stefan.klug@...asonboard.com>
Cc: Dafna Hirschfeld <dafna@...tmail.com>,  Laurent Pinchart
 <laurent.pinchart@...asonboard.com>,  Heiko Stuebner <heiko@...ech.de>,
  Paul Elder <paul.elder@...asonboard.com>,  Jacopo Mondi
 <jacopo.mondi@...asonboard.com>,  Ondrej Jirman <megi@....cz>,
  linux-media@...r.kernel.org,  linux-rockchip@...ts.infradead.org,
  linux-arm-kernel@...ts.infradead.org,  linux-kernel@...r.kernel.org
Subject: Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption

Hi Stefan,

Stefan Klug <stefan.klug@...asonboard.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 &reg_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,  &reg_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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ