QCOM 从CPU 启动地址

1. QCOM 多核起机流程

根据ARM官方文档的说明, 主CPU在作内存,系统资源初始化的过程中, 从CPU可以处于上电或者不上电状态。我是一个好奇心很强的人,想在内核的代码中找到QCOM多核是如何实现的。


经过几天的学习,在LK之后,主CPU上电,其他CPU未上电。在主CPU调用start_kernel-》rest_init-》kernel_init_freeable。(init/main.c)

在kernel_init_freeable中,smp_prepare_cpus(setup_max_cpus);  smp_init() 是两个重要SMP设置函数。


2. smp_prepare_cpus


arch/arm64/kernel/smp.c


void __init smp_prepare_cpus(unsigned int max_cpus)
{
        int err;
        unsigned int cpu, ncores = num_possible_cpus();

        init_cpu_topology();

        smp_store_cpu_info(smp_processor_id());

        /*
         * are we trying to boot more cores than exist?
         */
        if (max_cpus > ncores)
                max_cpus = ncores;

        /* Don't bother if we're effectively UP */
        if (max_cpus <= 1)
                return;

        /*
         * Initialise the present map (which describes the set of CPUs
         * actually populated at the present time) and release the
         * secondaries from the bootloader.
         *
         * Make sure we online at most (max_cpus - 1) additional CPUs.
         */
        max_cpus--;
        for_each_possible_cpu(cpu) {
                if (max_cpus == 0)
                        break;

                if (cpu == smp_processor_id())
                        continue;

                if (!cpu_ops[cpu])
                        continue;

                err = cpu_ops[cpu]->cpu_prepare(cpu);
                if (err)
                        continue;

                set_cpu_present(cpu, true);
                max_cpus--;
        }
}


cpu_ops 如何得到。


根据device tree


 cpu@0 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a53";
                        reg = <0x0>;
                        enable-method = "qcom,8994-arm-cortex-acc";
                        qcom,acc = <0xa>;
                        next-level-cache = <0xb>;
                        linux,phandle = <0x2>;
                        phandle = <0x2>;

                        l2-cache {
                                compatible = "arm,arch-cache";
                                cache-level = <0x2>;
                                power-domain = <0xc>;
                                linux,phandle = <0xb>;
                                phandle = <0xb>;

                                l2-tlb {
                                        qcom,dump-size = <0x4000>;
                                        linux,phandle = <0x113>;
                                        phandle = <0x113>;
                                };
                        };
                };

drivers/soc/qcom/cpu_ops.c

static const struct cpu_operations msm8994_cortex_a_ops = {
        .name           = "qcom,8994-arm-cortex-acc",
        .cpu_init       = msm_cpu_init,
        .cpu_prepare    = msm_cpu_prepare,
        .cpu_boot       = msm8994_cpu_boot,
        .cpu_postboot   = msm_cpu_postboot,
#ifdef CONFIG_HOTPLUG_CPU
        .cpu_die        = msm_wfi_cpu_die,
#endif
        .cpu_suspend       = msm_pm_collapse,
};
CPU_METHOD_OF_DECLARE(msm8994_cortex_a_ops, &msm8994_cortex_a_ops);



cpu_prepare,将调用msm_cpu_prepare,设置CPU reset的地址


static int __init msm_cpu_prepare(unsigned int cpu)
{
        u64 mpidr_el1 = cpu_logical_map(cpu);

        if (scm_is_mc_boot_available()) {

                if (mpidr_el1 & ~MPIDR_HWID_BITMASK) {
                        pr_err("CPU%d:Failed to set boot address\n", cpu);
                        return -ENOSYS;
                }

                if (scm_set_boot_addr_mc(virt_to_phys(secondary_holding_pen),  // cpu reset之后的地址
                                BIT(MPIDR_AFFINITY_LEVEL(mpidr_el1, 0)),
                                BIT(MPIDR_AFFINITY_LEVEL(mpidr_el1, 1)),
                                BIT(MPIDR_AFFINITY_LEVEL(mpidr_el1, 2)),
                                SCM_FLAG_COLDBOOT_MC)) {
                        pr_warn("CPU%d:Failed to set boot address\n", cpu);
                        return -ENOSYS;
                }

        } else {
                if (scm_set_boot_addr(virt_to_phys(secondary_holding_pen),
                        cold_boot_flags[cpu])) {
                        pr_warn("Failed to set CPU %u boot address\n", cpu);
                        return -ENOSYS;
                }
        }

3. smp_init

kernel/smp.c

/* Called by boot processor to activate the rest. */
void __init smp_init(void)
{
        unsigned int cpu;

        idle_threads_init();

        /* FIXME: This should be done in userspace --RR */
        for_each_present_cpu(cpu) {
                if (num_online_cpus() >= setup_max_cpus)
                        break;
                if (!cpu_online(cpu) && boot_cpu(cpu))
                        cpu_up(cpu);
        }

        free_boot_cpu_mask();

        /* Any cleanup work */
        printk(KERN_INFO "Brought up %ld CPUs\n", (long)num_online_cpus());
        smp_cpus_done(setup_max_cpus);
}

kernel/cpu.c

int __cpuinit cpu_up(unsigned int cpu)
{
        int err = 0;

        cpu_maps_update_begin();

        if (cpu_hotplug_disabled) {
                err = -EBUSY;
                goto out;
        }

        err = _cpu_up(cpu, 0);

out:
        cpu_maps_update_done();
        return err;
}



/* Requires cpu_add_remove_lock to be held */
static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
{
        int ret, nr_calls = 0;
        void *hcpu = (void *)(long)cpu;
        unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
        struct task_struct *idle;

        cpu_hotplug_begin();

.......


        /* Arch-specific enabling code. */
        ret = __cpu_up(cpu, idle);
        if (ret != 0)
                goto out_notify;
        BUG_ON(!cpu_online(cpu));

        /* Wake the per cpu threads */
        smpboot_unpark_threads(cpu);

        /* Now call notifier in preparation. */
        cpu_notify(CPU_ONLINE | mod, hcpu);

out_notify:
        if (ret != 0)
                __cpu_notify(CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL);
out:
        cpu_hotplug_done();
        trace_sched_cpu_hotplug(cpu, ret, 1);

        return ret;
}

arch/arm64/kernel/smp.c

int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
{
        int ret;

        /*
         * We need to tell the secondary core where to find its stack and the
         * page tables.
         */
        secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
        __flush_dcache_area(&secondary_data, sizeof(secondary_data));

        /*
         * Now bring the CPU into our world.
         */
        ret = boot_secondary(cpu, idle);
        if (ret == 0) {
                /*
                 * CPU was successfully started, wait for it to come online or
                 * time out.
                 */
                wait_for_completion_timeout(&cpu_running,
                                            msecs_to_jiffies(1000));

                if (!cpu_online(cpu)) {
                        pr_crit("CPU%u: failed to come online\n", cpu);
                        ret = -EIO;
                }
        } else {
                pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
        }

        secondary_data.stack = NULL;

        return ret;
}


static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
{
        if (cpu_ops[cpu]->cpu_boot)
                return cpu_ops[cpu]->cpu_boot(cpu);

        return -EOPNOTSUPP;
}

根据以上结果,cpu_boot将是msm8994_cpu_boot,


drivers/soc/qcom/cpu_ops.c

static int msm8994_cpu_boot(unsigned int cpu)
{
        int ret = 0;

        if (per_cpu(cold_boot_done, cpu) == false) {
                if (of_board_is_sim()) {
                        ret = msm_unclamp_secondary_arm_cpu_sim(cpu);
                        if (ret)
                                return ret;
                } else {
                        ret = msm8994_unclamp_secondary_arm_cpu(cpu);
                        if (ret)
                                return ret;
                }
                per_cpu(cold_boot_done, cpu) = true;
        }
        return secondary_pen_release(cpu);
}

int msm8994_unclamp_secondary_arm_cpu(unsigned int cpu)
{

        int ret = 0;
        struct device_node *cpu_node, *acc_node, *l2_node, *l2ccc_node;
        void __iomem *acc_reg, *ldo_bhs_reg;

        cpu_node = of_get_cpu_node(cpu, NULL);
        if (!cpu_node)
                return -ENODEV;

        acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
        if (!acc_node) {
                        ret = -ENODEV;
                        goto out_acc;
        }

        l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0);
        if (!l2_node) {
                ret = -ENODEV;
                goto out_l2;
        }

        l2ccc_node = of_parse_phandle(l2_node, "power-domain", 0);
        if (!l2ccc_node) {
                ret = -ENODEV;
                goto out_l2;
        }

        /*
         * Ensure L2-cache of the CPU is powered on before
         * unclamping cpu power rails.
         */

        ret = power_on_l2_cache(l2ccc_node, cpu);
        if (ret) {
                pr_err("L2 cache power up failed for CPU%d\n", cpu);
                goto out_l2ccc;
        }

        ldo_bhs_reg = of_iomap(acc_node, 0);
        if (!ldo_bhs_reg) {
                ret = -ENOMEM;
                goto out_bhs_reg;
        }

        acc_reg = of_iomap(acc_node, 1);
        if (!acc_reg) {
                ret = -ENOMEM;
                goto out_acc_reg;
        }

        /* Assert head switch enable few */
        writel_relaxed(0x00000001, acc_reg + CPU_PWR_GATE_CTL);
        mb();
        udelay(1);

        /* Assert head switch enable rest */
        writel_relaxed(0x00000003, acc_reg + CPU_PWR_GATE_CTL);
        mb();
      /* De-assert coremem clamp. This is asserted by default */
        writel_relaxed(0x00000079, acc_reg + CPU_PWR_CTL);
        mb();
        udelay(2);

        /* Close coremem array gdhs */
        writel_relaxed(0x0000007D, acc_reg + CPU_PWR_CTL);
        mb();
        udelay(2);

        /* De-assert clamp */
        writel_relaxed(0x0000003D, acc_reg + CPU_PWR_CTL);
        mb();

        /* De-assert clamp */
        writel_relaxed(0x0000003C, acc_reg + CPU_PWR_CTL);
        mb();
        udelay(1);

        /* De-assert core0 reset */
        writel_relaxed(0x0000000C, acc_reg + CPU_PWR_CTL);
        mb();

        /* Assert PWRDUP */
        writel_relaxed(0x0000008C, acc_reg + CPU_PWR_CTL);
        mb();

        iounmap(acc_reg);
out_acc_reg:
        iounmap(ldo_bhs_reg);
out_bhs_reg:
        of_node_put(l2ccc_node);
out_l2ccc:
        of_node_put(l2_node);
out_l2:
        of_node_put(acc_node);
out_acc:
        of_node_put(cpu_node);

        return ret;
}

其中一些寄存器的操作我不太了解,大致是给CPU上电。 CPU 上电后,将从secondary_holding_pen执行。




你可能感兴趣的:(Android,Kernel)