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/cpufreq.h>
 22#include <linux/module.h>
 23#include <linux/platform_device.h>
 24#include <linux/pm_opp.h>
 25#include <linux/scpi_protocol.h>
 26#include <linux/types.h>
 27
 28#include "arm_big_little.h"
 29
 30static struct scpi_ops *scpi_ops;
 31
 32static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev)
 33{
 34	int domain = topology_physical_package_id(cpu_dev->id);
 35
 36	if (domain < 0)
 37		return ERR_PTR(-EINVAL);
 38	return scpi_ops->dvfs_get_info(domain);
 39}
 40
 41static int scpi_opp_table_ops(struct device *cpu_dev, bool remove)
 42{
 43	int idx, ret = 0;
 44	struct scpi_opp *opp;
 45	struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
 46
 47	if (IS_ERR(info))
 48		return PTR_ERR(info);
 49
 50	if (!info->opps)
 51		return -EIO;
 52
 53	for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
 54		if (remove)
 55			dev_pm_opp_remove(cpu_dev, opp->freq);
 56		else
 57			ret = dev_pm_opp_add(cpu_dev, opp->freq,
 58					     opp->m_volt * 1000);
 59		if (ret) {
 60			dev_warn(cpu_dev, "failed to add opp %uHz %umV\n",
 61				 opp->freq, opp->m_volt);
 62			while (idx-- > 0)
 63				dev_pm_opp_remove(cpu_dev, (--opp)->freq);
 64			return ret;
 65		}
 66	}
 67	return ret;
 68}
 69
 70static int scpi_get_transition_latency(struct device *cpu_dev)
 71{
 72	struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
 73
 74	if (IS_ERR(info))
 75		return PTR_ERR(info);
 76	return info->latency;
 77}
 78
 79static int scpi_init_opp_table(struct device *cpu_dev)
 80{
 81	return scpi_opp_table_ops(cpu_dev, false);
 82}
 83
 84static void scpi_free_opp_table(struct device *cpu_dev)
 85{
 86	scpi_opp_table_ops(cpu_dev, true);
 87}
 88
 89static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
 90	.name	= "scpi",
 91	.get_transition_latency = scpi_get_transition_latency,
 92	.init_opp_table = scpi_init_opp_table,
 93	.free_opp_table = scpi_free_opp_table,
 94};
 95
 96static int scpi_cpufreq_probe(struct platform_device *pdev)
 97{
 98	scpi_ops = get_scpi_ops();
 99	if (!scpi_ops)
100		return -EIO;
101
102	return bL_cpufreq_register(&scpi_cpufreq_ops);
103}
104
105static int scpi_cpufreq_remove(struct platform_device *pdev)
106{
107	bL_cpufreq_unregister(&scpi_cpufreq_ops);
108	scpi_ops = NULL;
109	return 0;
110}
111
112static struct platform_driver scpi_cpufreq_platdrv = {
113	.driver = {
114		.name	= "scpi-cpufreq",
115		.owner	= THIS_MODULE,
116	},
117	.probe		= scpi_cpufreq_probe,
118	.remove		= scpi_cpufreq_remove,
119};
120module_platform_driver(scpi_cpufreq_platdrv);
121
122MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
123MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
124MODULE_LICENSE("GPL v2");