Re: FYI: i.MX8MP ISP (RKISP1) MI registers corruption

Stefan Klug
2025-07-23T10:20:01Z

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 &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