linux cpufreq interactive调频代码实现

Android使用的调频governor都是interactive,我们就以interactive为例,
看下整个调频的代码实现。


我们沿着driver初始化往下分析代码流程,高通平台对应的驱动文件qcom-cpufreq.c。
初始化首先调用:msm_cpufreq_register
//依次初始化每个cpu suspend_mutex和device_suspended,注册驱动,最后注册notify调用
static int __init msm_cpufreq_register(void)
{
	int cpu, rc;

	for_each_possible_cpu(cpu) {
		mutex_init(&(per_cpu(cpufreq_suspend, cpu).suspend_mutex));
		per_cpu(cpufreq_suspend, cpu).device_suspended = 0;
	}

	rc = platform_driver_probe(&msm_cpufreq_plat_driver,
				   msm_cpufreq_probe);
	if (rc < 0) {
		/* Unblock hotplug if msm-cpufreq probe fails */
		unregister_hotcpu_notifier(&msm_cpufreq_cpu_notifier);
		for_each_possible_cpu(cpu)
			mutex_destroy(&(per_cpu(cpufreq_suspend, cpu).
					suspend_mutex));
		return rc;
	}

	register_pm_notifier(&msm_cpufreq_pm_notifier);
	return cpufreq_register_driver(&msm_cpufreq_driver);
}

//解析 qcom,msm-cpufreq 节点,获取cpu clock和频率信息
static int __init msm_cpufreq_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	char clk_name[] = "cpu??_clk";
	char tbl_name[] = "qcom,cpufreq-table-??";
	struct clk *c;
	int cpu;
	struct cpufreq_frequency_table *ftbl;

	l2_clk = devm_clk_get(dev, "l2_clk");
	if (IS_ERR(l2_clk))
		l2_clk = NULL;

	for_each_possible_cpu(cpu) {
		snprintf(clk_name, sizeof(clk_name), "cpu%d_clk", cpu);
		c = devm_clk_get(dev, clk_name);
		if (IS_ERR(c))
			return PTR_ERR(c);
		cpu_clk[cpu] = c;
	}
	hotplug_ready = true;

	/* Use per-policy governor tunable for some targets */
	//该属性表示每个policy使用各自的governor
	if (of_property_read_bool(dev->of_node, "qcom,governor-per-policy"))
		msm_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;

	/* Parse commong cpufreq table for all CPUs */
	ftbl = cpufreq_parse_dt(dev, "qcom,cpufreq-table", 0);
	if (!IS_ERR(ftbl)) {
		for_each_possible_cpu(cpu)
			per_cpu(freq_table, cpu) = ftbl;
		return 0;
	}

	/*
	 * No common table. Parse individual tables for each unique
	 * CPU clock.
	 */
	//以8核四个大核,四个小核为例,四个大核公用一个频率表,四个小核公用一个频率表
	for_each_possible_cpu(cpu) {
		snprintf(tbl_name, sizeof(tbl_name),
			 "qcom,cpufreq-table-%d", cpu);
		ftbl = cpufreq_parse_dt(dev, tbl_name, cpu);

		/* CPU0 must contain freq table */
		if (cpu == 0 && IS_ERR(ftbl)) {
			dev_err(dev, "Failed to parse CPU0's freq table\n");
			return PTR_ERR(ftbl);
		}
		if (cpu == 0) {
			per_cpu(freq_table, cpu) = ftbl;
			continue;
		}

		if (cpu_clk[cpu] != cpu_clk[cpu - 1] && IS_ERR(ftbl)) {
			dev_err(dev, "Failed to parse CPU%d's freq table\n",
				cpu);
			return PTR_ERR(ftbl);
		}

		/* Use previous CPU's table if it shares same clock */
		if (cpu_clk[cpu] == cpu_clk[cpu - 1]) {
			if (!IS_ERR(ftbl)) {
				dev_warn(dev, "Conflicting tables for CPU%d\n",
					 cpu);
				devm_kfree(dev, ftbl);
			}
			ftbl = per_cpu(freq_table, cpu - 1);
		}
		per_cpu(freq_table, cpu) = ftbl;
	}

	return 0;
}

//接下来看下cpufreq_register_driver,这个函数主要用来向cpufreq core部分
//注册驱动并初始化每个cpu 调频策略

int cpufreq_register_driver(struct cpufreq_driver *driver_data)
{
	unsigned long flags;
	int ret;

	if (cpufreq_disabled())
		return -ENODEV;
	/*
	setpolicy跟target驱动必须实现这两个函数中的其中一个,
	如果不支持通过governor选择合适的运行频率,则实现setpolicy回调函数,
	这样系统只能支持CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE这两种工作策略。
	反之,实现target回调函数,通过target回调设定governor所需要的频率
	*/
	if (!driver_data || !driver_data->verify || !driver_data->init ||
	    !(driver_data->setpolicy || driver_data->target_index ||
		    driver_data->target))
		return -EINVAL;

	pr_debug("trying to register driver %s\n", driver_data->name);

	if (driver_data->setpolicy)
		driver_data->flags |= CPUFREQ_CONST_LOOPS;

	write_lock_irqsave(&cpufreq_driver_lock, flags);
	if (cpufreq_driver) {
		write_unlock_irqrestore(&cpufreq_driver_lock, flags);
		return -EEXIST;
	}
	//驱动赋值,供cpufreq core使用
	cpufreq_driver = driver_data;
	write_unlock_irqrestore(&cpufreq_driver_lock, flags);
	//注册cpu hotplug 处理函数
	register_hotcpu_notifier(&cpufreq_cpu_notifier);

	get_online_cpus();
	//调用驱动实现的add_dev,初始化每个cpu 调频策略
	ret = subsys_interface_register(&cpufreq_interface);
	put_online_cpus();
	if (ret)
		goto err_null_driver;

	if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) {
		int i;
		ret = -ENODEV;

		/* check for at least one working CPU */
		for (i = 0; i < nr_cpu_ids; i++)
			if (cpu_possible(i) && per_cpu(cpufreq_cpu_data, i)) {
				ret = 0;
				break;
			}

		/* if all ->init() calls failed, unregister */
		if (ret) {
			pr_debug("no CPU initialized for driver %s\n",
							driver_data->name);
			goto err_if_unreg;
		}
	}

	pr_info("driver %s up and running\n", driver_data->name);

	return 0;
err_if_unreg:
	subsys_interface_unregister(&cpufreq_interface);
err_null_driver:
	unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
	write_lock_irqsave(&cpufreq_driver_lock, flags);
	cpufreq_driver = NULL;
	write_unlock_irqrestore(&cpufreq_driver_lock, flags);
	return ret;
}
//调用cpufreq_add_dev,依次初始化每个cpu core
int subsys_interface_register(struct subsys_interface *sif)
{
	struct bus_type *subsys;
	struct subsys_dev_iter iter;
	struct device *dev;

	if (!sif || !sif->subsys)
		return -ENODEV;

	subsys = bus_get(sif->subsys);
	if (!subsys)
		return -EINVAL;

	mutex_lock(&subsys->p->mutex);
	list_add_tail(&sif->node, &subsys->p->interfaces);
	if (sif->add_dev) {
		subsys_dev_iter_init(&iter, subsys, NULL, NULL);
		while ((dev = subsys_dev_iter_next(&iter)))
			sif->add_dev(dev, sif);
		subsys_dev_iter_exit(&iter);
	}
	mutex_unlock(&subsys->p->mutex);

	return 0;
}

//每个cpu核心的初始化工作,为cpu建立policy,执行policy对应的governor函数,启动governor,
//建立sysfs文件系统
static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
			     bool frozen)
{
	unsigned int j, cpu = dev->id;
	int ret = -ENOMEM;
	struct cpufreq_policy *policy;
	unsigned long flags;
#ifdef CONFIG_HOTPLUG_CPU
	struct cpufreq_policy *tpolicy;
	struct cpufreq_governor *gov;
#endif

	if (cpu_is_offline(cpu))
		return 0;

	pr_debug("adding CPU %u\n", cpu);

#ifdef CONFIG_SMP
	/* check whether a different CPU already registered this
	 * CPU because it is in the same boat. */
	//cpufreq_cpu_get获取cpu对应的policy,如果多个cpu公用一个policy,那么之前共用policy //cpu初始化时已经为该cpu建立好了policy 
	policy = cpufreq_cpu_get(cpu);
	if (unlikely(policy)) {
		cpufreq_cpu_put(policy);
		return 0;
	}
#endif

	if (!down_read_trylock(&cpufreq_rwsem))
		return 0;

#ifdef CONFIG_HOTPLUG_CPU
	/* Check if this cpu was hot-unplugged earlier and has siblings */
	//如果当前cpu之前被拔出,并且此cpu跟其他未移除cpu共用policy,则无需创建policy,只需要
	//将此cpu加入policy管理,并启动对应的governor即可
	read_lock_irqsave(&cpufreq_driver_lock, flags);
	list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) {
		if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) {//当前cpu属于此policy管理的cpu
			read_unlock_irqrestore(&cpufreq_driver_lock, flags);
			ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev);//给cpu创建policy,执行governor start/limit函数
			up_read(&cpufreq_rwsem);
			return ret;
		}
	}
	read_unlock_irqrestore(&cpufreq_driver_lock, flags);
#endif

	if (frozen)//cpu 之前处于冻结状态,无需申请policy结构
		/* Restore the saved policy when doing light-weight init */
		policy = cpufreq_policy_restore(cpu);
	else//非冻结时,分配一个新的policy结构体,并初始化
		policy = cpufreq_policy_alloc();

	if (!policy)
		goto nomem_out;


	/*
	 * In the resume path, since we restore a saved policy, the assignment
	 * to policy->cpu is like an update of the existing policy, rather than
	 * the creation of a brand new one. So we need to perform this update
	 * by invoking update_policy_cpu().
	 */
	//对于之前处于frozen cpu,需要更新其policy
	if (frozen && cpu != policy->cpu)
		update_policy_cpu(policy, cpu);
	else
		policy->cpu = cpu;
		
	//设定governor默认为performance
	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
	cpumask_copy(policy->cpus, cpumask_of(cpu));

	init_completion(&policy->kobj_unregister);
	INIT_WORK(&policy->update, handle_update);

	/* call driver. From then on the cpufreq must be able
	 * to accept all calls to ->verify and ->setpolicy for this CPU
	 */
	//调用驱动的init函数msm_cpufreq_init
	ret = cpufreq_driver->init(policy);
	if (ret) {
		pr_debug("initialization failed\n");
		goto err_set_policy_cpu;
	}
	//获取cpu当前频率
	if (cpufreq_driver->get) {
		policy->cur = cpufreq_driver->get(policy->cpu);
		if (!policy->cur) {
			pr_err("%s: ->get() failed\n", __func__);
			goto err_get_freq;
		}
	}

	/* related cpus should atleast have policy->cpus */
	//设定policy管理的cpu
	cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus);

	/*
	 * affected cpus must always be the one, which are online. We aren't
	 * managing offline cpus here.
	 */
	//policy 管理的online cpu
	cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);

	policy->user_policy.min = policy->min;
	policy->user_policy.max = policy->max;

	blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
				     CPUFREQ_START, policy);
	
//如果是hot-plug加入的cpu,找出它上次使用的governor
#ifdef CONFIG_HOTPLUG_CPU
	gov = __find_governor(per_cpu(cpufreq_policy_save, cpu).gov);
	if (gov) {
		policy->governor = gov;
		pr_debug("Restoring governor %s for cpu %d\n",
		       policy->governor->name, cpu);
	}
	if (per_cpu(cpufreq_policy_save, cpu).min) {
		policy->min = per_cpu(cpufreq_policy_save, cpu).min;
		policy->user_policy.min = policy->min;
	}
	if (per_cpu(cpufreq_policy_save, cpu).max) {
		policy->max = per_cpu(cpufreq_policy_save, cpu).max;
		policy->user_policy.max = policy->max;
	}
	pr_debug("Restoring CPU%d user policy min %d and max %d\n", cpu,
		 policy->min, policy->max);
#endif

	//建立cpufreq文件节点,然后在它的下面再建立一系列节点,
    //用户可以通过这些文件节点控制该policy的一些参数
	if (!frozen) {
		ret = cpufreq_add_dev_interface(policy, dev);
		if (ret)
			goto err_out_unregister;
		blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
				CPUFREQ_CREATE_POLICY, policy);
	}

	write_lock_irqsave(&cpufreq_driver_lock, flags);
	list_add(&policy->policy_list, &cpufreq_policy_list);
	write_unlock_irqrestore(&cpufreq_driver_lock, flags);
	
	//policy 初始化,主要是启动对应的governor
	cpufreq_init_policy(policy);

	write_lock_irqsave(&cpufreq_driver_lock, flags);
	//设定policy下所有cpu 的policy,避免重复创建policy
	for_each_cpu(j, policy->cpus)
		per_cpu(cpufreq_cpu_data, j) = policy;
	write_unlock_irqrestore(&cpufreq_driver_lock, flags);

	kobject_uevent(&policy->kobj, KOBJ_ADD);
	up_read(&cpufreq_rwsem);

	pr_debug("initialization complete\n");

	return 0;

err_out_unregister:
	write_lock_irqsave(&cpufreq_driver_lock, flags);
	for_each_cpu(j, policy->cpus)
		per_cpu(cpufreq_cpu_data, j) = NULL;
	write_unlock_irqrestore(&cpufreq_driver_lock, flags);

err_get_freq:
	if (cpufreq_driver->exit)
		cpufreq_driver->exit(policy);
err_set_policy_cpu:
	if (frozen)
		cpufreq_policy_put_kobj(policy);
	cpufreq_policy_free(policy);

nomem_out:
	up_read(&cpufreq_rwsem);

	return ret;
}

static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
				  unsigned int cpu, struct device *dev)
{
	int ret = 0;
	unsigned long flags;

	if (has_target()) {
		ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
		if (ret) {
			pr_err("%s: Failed to stop governor for CPU%u, policy CPU%u\n",
			       __func__, cpu, policy->cpu);
			return ret;
		}
	}

	down_write(&policy->rwsem);

	write_lock_irqsave(&cpufreq_driver_lock, flags);
	//将cpu加入policy管理
	cpumask_set_cpu(cpu, policy->cpus);
	per_cpu(cpufreq_cpu_data, cpu) = policy;
	write_unlock_irqrestore(&cpufreq_driver_lock, flags);

	up_write(&policy->rwsem);
	//启动governor,并设定cpu工作频率
	if (has_target()) {
		if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) ||
			(ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) {
			pr_err("%s: Failed to start governor for CPU%u, policy CPU%u\n",
			       __func__, cpu, policy->cpu);
			return ret;
		}
	}
	//创建cpu对应的sysfs节点
	return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
}

//每颗cpu都要调用init,因为使用的是cpu上的本地数据,不需要锁保护
/*
设定该cpu的最大和最小工作频率
设定该policy的最大和最小工作频率
设定该policy可供调节的频率档位
设定cpu调节频率时的延迟时间特性
该policy可以管理的cpu个数(policy->cpus)
*/
static int msm_cpufreq_init(struct cpufreq_policy *policy)
{
	int cur_freq;
	int index;
	int ret = 0;
	//每个cpu频率数组
	struct cpufreq_frequency_table *table =
			per_cpu(freq_table, policy->cpu);
	int cpu;

	/*
	 * In some SoC, some cores are clocked by same source, and their
	 * frequencies can not be changed independently. Find all other
	 * CPUs that share same clock, and mark them as controlled by
	 * same policy.
	 */
	//共享同一个时钟源的cpu,有同一个policy管理
	for_each_possible_cpu(cpu)
		if (cpu_clk[cpu] == cpu_clk[policy->cpu])
			cpumask_set_cpu(cpu, policy->cpus);
	//遍历频率表找到最大最小频率,更新policy最大、最小频率
	if (cpufreq_frequency_table_cpuinfo(policy, table))
		pr_err("cpufreq: failed to get policy min/max\n");
	//获取当前频率
	cur_freq = clk_get_rate(cpu_clk[policy->cpu])/1000;
	
    /*
	CPUFREQ_RELATION_L:取freq table中大于或等于new_freq的频率中最小的一个频率,返回index,再由index得到new freq
	CPUFREQ_RELATION_H表示取即是取freq table中小于或等于new_freq的频率中最大的一个频率
	*/
	if (cpufreq_frequency_table_target(policy, table, cur_freq,
	    CPUFREQ_RELATION_H, &index) &&
	    cpufreq_frequency_table_target(policy, table, cur_freq,
	    CPUFREQ_RELATION_L, &index)) {
		pr_info("cpufreq: cpu%d at invalid freq: %d\n",
				policy->cpu, cur_freq);
		return -EINVAL;
	}
	/*
	 * Call set_cpu_freq unconditionally so that when cpu is set to
	 * online, frequency limit will always be updated.
	 */
	//设定cpu频率
	ret = set_cpu_freq(policy, table[index].frequency,
			   table[index].driver_data);
	if (ret)
		return ret;
	pr_debug("cpufreq: cpu%d init at %d switching to %d\n",
			policy->cpu, cur_freq, table[index].frequency);
	policy->cur = table[index].frequency;
	cpufreq_frequency_table_get_attr(table, policy->cpu);

	return 0;
}


以上的整个过程从驱动初始化,一步步到governor初始化,接下来开始详细分析interactive对应的代码流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值