Überprüfen Sie die herstellerabhängige stromsparende Implementierungsmethode des Linux-Kernels basierend auf i.mx6.

zunaechst

https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.1.52.tar.gz

Basierend auf dem Code von mach-imx / pm-imx6 werde ich zusammenfassen, was bei der Implementierung des Energiesparmodus zu tun ist.

TL;DR

arch/arm/mach-imx/pm-imx6.c


static const struct platform_suspend_ops imx6q_pm_ops = {
    .enter = imx6q_pm_enter,
    .valid = imx6q_pm_valid,
};

imx6q_pm_valid

Es unterstützt STANDBY und MEM.

arch/arm/mach-imx/pm-imx6.c


static int imx6q_pm_valid(suspend_state_t state)
{
        return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM);
}

imx6q_pm_enter

Der Inhalt von enter ist auch sehr einfach und leicht zu verstehen!

arch/arm/mach-imx/pm-imx6.c


static int imx6q_pm_enter(suspend_state_t state)
{
        switch (state) {
        case PM_SUSPEND_STANDBY:
                imx6q_set_lpm(STOP_POWER_ON);
                imx6q_set_int_mem_clk_lpm(true);
                imx_gpc_pre_suspend(false);
                if (cpu_is_imx6sl())
                        imx6sl_set_wait_clk(true);
                /* Zzz ... */
                cpu_do_idle();
                if (cpu_is_imx6sl())
                        imx6sl_set_wait_clk(false);
                imx_gpc_post_resume();
                imx6q_set_lpm(WAIT_CLOCKED);
                break;
        case PM_SUSPEND_MEM:
                imx6q_set_lpm(STOP_POWER_OFF);
                imx6q_set_int_mem_clk_lpm(false);
                imx6q_enable_wb(true);
                /*
                 * For suspend into ocram, asm code already take care of
                 * RBC setting, so we do NOT need to do that here.
                 */
                if (!imx6_suspend_in_ocram_fn)
                        imx6_enable_rbc(true);
                imx_gpc_pre_suspend(true);
                imx_anatop_pre_suspend();
                /* Zzz ... */
                cpu_suspend(0, imx6q_suspend_finish);
                if (cpu_is_imx6q() || cpu_is_imx6dl())
                        imx_smp_prepare();
                imx_anatop_post_resume();
                imx_gpc_post_resume();
                imx6_enable_rbc(false);
                imx6q_enable_wb(false);
                imx6q_set_int_mem_clk_lpm(true);
                imx6q_set_lpm(WAIT_CLOCKED);
                break;
        default:
                return -EINVAL;
        }

        return 0;
}

cpu_do_idle() cpu_do_idle (); ist eine einfache Implementierung, die nur wfi aufruft.

arch/arm64/mm/proc.S


/*
 *      cpu_do_idle()
 *
 *      Idle the processor (wait for interrupt).
 */
ENTRY(cpu_do_idle)
        dsb     sy                              // WFI may enter a low-power mode
        wfi
        ret
ENDPROC(cpu_do_idle)

cpu_suspend()

Andererseits scheint "cpu_suspend ()" die durch das Argument gegebene Funktion auszuführen.

arch/arm/kernel/suspend.c


#ifdef CONFIG_MMU
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
        struct mm_struct *mm = current->active_mm;
        u32 __mpidr = cpu_logical_map(smp_processor_id());
        int ret;

        if (!idmap_pgd)
                return -EINVAL;

        /*
         * Provide a temporary page table with an identity mapping for
         * the MMU-enable code, required for resuming.  On successful
         * resume (indicated by a zero return code), we need to switch
         * back to the correct page tables.
         */
        ret = __cpu_suspend(arg, fn, __mpidr);
        if (ret == 0) {
                cpu_switch_mm(mm->pgd, mm);
                local_flush_bp_all();
                local_flush_tlb_all();
        }

        return ret;
}
#else
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
        u32 __mpidr = cpu_logical_map(smp_processor_id());
        return __cpu_suspend(arg, fn, __mpidr);
}
#define idmap_pgd       NULL
#endif

imx6q_suspend_finish()

Das Argument zu diesem Zeitpunkt lautet "imx6q_suspend_finish ()".

arch/arm/mach-imx/pm-imx6.c


static int imx6q_suspend_finish(unsigned long val)
{
        if (!imx6_suspend_in_ocram_fn) {
                cpu_do_idle();
        } else {
                /*
                 * call low level suspend function in ocram,
                 * as we need to float DDR IO.
                 */
                local_flush_tlb_all();
                imx6_suspend_in_ocram_fn(suspend_ocram_base);
        }

        return 0;
}

imx6_suspend_in_orcam_fn()

Das von hier aufgerufene imx6_suspend_in_orcam_fn () wird durch einen Funktionszeiger definiert.

arch/arm/mach-imx/pm-imx6.c


static void __iomem *ccm_base;
static void __iomem *suspend_ocram_base;
static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase);

Zum Zeitpunkt der Initialisierung beim Start wird die Funktion imx6_suspend auf ocram kopiert.

Wie Sie dem Code entnehmen können, ist ocram mmi-sram, dh ein integrierter SRAM. Die hier platzierten Daten können grundsätzlich auch dann referenziert werden, wenn sich DDR im Speicheraktualisierungszustand befindet. Hier gibt es eine Schlafimplementierung.

arch/arm/mach-imx/pm-imx6.c


static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
{
        node = of_find_compatible_node(NULL, NULL, "mmio-sram");
        if (!node) {
                pr_warn("%s: failed to find ocram node!\n", __func__);
                return -ENODEV;
        }

        pdev = of_find_device_by_node(node);
        if (!pdev) {
                pr_warn("%s: failed to find ocram device!\n", __func__);
                ret = -ENODEV;
                goto put_node;
        }

        ocram_pool = dev_get_gen_pool(&pdev->dev);
        if (!ocram_pool) {
                pr_warn("%s: ocram pool unavailable!\n", __func__);
                ret = -ENODEV;
                goto put_node;
        }

        ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE);
        if (!ocram_base) {
                pr_warn("%s: unable to alloc ocram!\n", __func__);
                ret = -ENOMEM;
                goto put_node;
        }

        ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);

        suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
                MX6Q_SUSPEND_OCRAM_SIZE, false);

<Ausgelassen>
        imx6_suspend_in_ocram_fn = fncpy(
                suspend_ocram_base + sizeof(*pm_info),
                &imx6_suspend,
                MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));

fncpy()

Fncpy (), das eine Funktion in eine andere Speicheradresse kopiert, ist als eine Funktion definiert, die für allgemeine Zwecke verwendet werden kann.

arch/arm/include/asm/fncpy.h


/*
 * Minimum alignment requirement for the source and destination addresses
 * for function copying.
 */
#define FNCPY_ALIGN 8

#define fncpy(dest_buf, funcp, size) ({                                 \
        uintptr_t __funcp_address;                                      \
        typeof(funcp) __result;                                         \
                                                                        \
        asm("" : "=r" (__funcp_address) : "0" (funcp));                 \
                                                                        \
        /*                                                              \
         * Ensure alignment of source and destination addresses,        \
         * disregarding the function's Thumb bit:                       \
         */                                                             \
        BUG_ON((uintptr_t)(dest_buf) & (FNCPY_ALIGN - 1) ||             \
                (__funcp_address & ~(uintptr_t)1 & (FNCPY_ALIGN - 1))); \
                                                                        \
        memcpy(dest_buf, (void const *)(__funcp_address & ~1), size);   \
        flush_icache_range((unsigned long)(dest_buf),                   \
                (unsigned long)(dest_buf) + (size));                    \
                                                                        \
        asm("" : "=r" (__result)                                        \
                : "0" ((uintptr_t)(dest_buf) | (__funcp_address & 1))); \
                                                                        \
        __result;                                                       \
})

imx6_suspend()

Die Funktion "imx6_suspend" ist als Assembler enthalten.

Oh, ich habe es in Erinnerung behalten, mich selbst zu aktualisieren. Es wird hilfreich sein.

arch/arm/mach-imx/suspend-imx6.S


ENTRY(imx6_suspend)
        ldr     r1, [r0, #PM_INFO_PBASE_OFFSET]
        ldr     r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
        ldr     r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
        ldr     r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]

<Ausgelassen>
        /*
         * put DDR explicitly into self-refresh and
         * disable automatic power savings.
         */
        ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
        orr     r7, r7, #0x1
        str     r7, [r11, #MX6Q_MMDC_MAPSR]

<Ausgelassen>

        /* Zzz, enter stop mode */
        wfi
        nop
        nop
        nop
        nop

<Ausgelassen>

Zusammenfassung (Repost)

das ist alles.

Recommended Posts

Überprüfen Sie die herstellerabhängige stromsparende Implementierungsmethode des Linux-Kernels basierend auf i.mx6.
[Python] Implementierung der Nelder-Mead-Methode und Speichern von GIF-Bildern durch matplotlib