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

Stefan Klug stefan.klug at ideasonboard.com
Wed Jul 23 03:19:56 PDT 2025


Hi Krzysztof,

Quoting Krzysztof Hałasa (2025-07-21 15:16:28)
> Hi Stefan,
> 
> Stefan Klug <stefan.klug at 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



More information about the linux-arm-kernel mailing list