Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * System Control and Power Interface (SCPI) based CPUFreq Interface driver
  3 *
  4 * It provides necessary ops to arm_big_little cpufreq driver.
  5 *
  6 * Copyright (C) 2015 ARM Ltd.
  7 * Sudeep Holla <sudeep.holla@arm.com>
  8 *
  9 * This program is free software; you can redistribute it and/or modify
 10 * it under the terms of the GNU General Public License version 2 as
 11 * published by the Free Software Foundation.
 12 *
 13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 14 * kind, whether express or implied; without even the implied warranty
 15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16 * GNU General Public License for more details.
 17 */
 18
 19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 20
 21#include <linux/cpu.h>
 22#include <linux/cpufreq.h>
 23#include <linux/module.h>
 24#include <linux/platform_device.h>
 25#include <linux/pm_opp.h>
 26#include <linux/scpi_protocol.h>
 27#include <linux/types.h>
 28
 29#include "arm_big_little.h"
 30
 31static struct scpi_ops *scpi_ops;
 32
 33static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev)
 34{
 35	int domain = topology_physical_package_id(cpu_dev->id);
 36
 37	if (domain < 0)
 38		return ERR_PTR(-EINVAL);
 39	return scpi_ops->dvfs_get_info(domain);
 40}
 41
 42static int scpi_get_transition_latency(struct device *cpu_dev)
 43{
 44	struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
 45
 46	if (IS_ERR(info))
 47		return PTR_ERR(info);
 48	return info->latency;
 49}
 50
 51static int scpi_init_opp_table(const struct cpumask *cpumask)
 52{
 53	int idx, ret;
 54	struct scpi_opp *opp;
 55	struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask));
 56	struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
 57
 58	if (IS_ERR(info))
 59		return PTR_ERR(info);
 60
 61	if (!info->opps)
 62		return -EIO;
 63
 64	for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
 65		ret = dev_pm_opp_add(cpu_dev, opp->freq, opp->m_volt * 1000);
 66		if (ret) {
 67			dev_warn(cpu_dev, "failed to add opp %uHz %umV\n",
 68				 opp->freq, opp->m_volt);
 69			while (idx-- > 0)
 70				dev_pm_opp_remove(cpu_dev, (--opp)->freq);
 71			return ret;
 72		}
 73	}
 74
 75	ret = dev_pm_opp_set_sharing_cpus(cpu_dev, cpumask);
 76	if (ret)
 77		dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
 78			__func__, ret);
 79	return ret;
 80}
 81
 82static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
 83	.name	= "scpi",
 84	.get_transition_latency = scpi_get_transition_latency,
 85	.init_opp_table = scpi_init_opp_table,
 86	.free_opp_table = dev_pm_opp_cpumask_remove_table,
 87};
 88
 89static int scpi_cpufreq_probe(struct platform_device *pdev)
 90{
 91	scpi_ops = get_scpi_ops();
 92	if (!scpi_ops)
 93		return -EIO;
 94
 95	return bL_cpufreq_register(&scpi_cpufreq_ops);
 96}
 97
 98static int scpi_cpufreq_remove(struct platform_device *pdev)
 99{
100	bL_cpufreq_unregister(&scpi_cpufreq_ops);
101	scpi_ops = NULL;
102	return 0;
103}
104
105static struct platform_driver scpi_cpufreq_platdrv = {
106	.driver = {
107		.name	= "scpi-cpufreq",
108	},
109	.probe		= scpi_cpufreq_probe,
110	.remove		= scpi_cpufreq_remove,
111};
112module_platform_driver(scpi_cpufreq_platdrv);
113
114MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
115MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
116MODULE_LICENSE("GPL v2");