blob: 96fe0bce1dea180320f91df3738a86da20caf7b4 [file] [log] [blame]
/*
* drivers/amlogic/cpufreq/meson-dvfs-id.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/topology.h>
#include <linux/delay.h>
#include <linux/amlogic/scpi_protocol.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/arm-smccc.h>
#define GET_DVFS_TABLE_INDEX 0x82000088
static char dvfs_id;
static struct class *dvfsid_cls;
static char *dvfsid_dev = "dvfs_id";
static unsigned int get_cpufreq_table_index(u64 function_id,
u64 arg0, u64 arg1, u64 arg2)
{
struct arm_smccc_res res;
arm_smccc_smc((unsigned long)function_id,
(unsigned long)arg0,
(unsigned long)arg1,
(unsigned long)arg2,
0, 0, 0, 0, &res);
return res.a0;
}
static ssize_t show_dvfsid(struct class *class,
struct class_attribute *attr, char *buf)
{
int ret = 0;
ret = sprintf(buf, "%d\n", dvfs_id);
return ret;
}
static ssize_t show_ringmsr(struct class *class,
struct class_attribute *attr, char *buf)
{
int ret = 0;
unsigned char ringinfo[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char str[512] = {0};
char *name[] = {"unknown", "cpu_ring0", "mail_ring1", "dmc_ring", "cpu_ring2", "iddee", "reserve", "iddcpu"};
int i = 0;
if (scpi_get_ring_value(ringinfo) != 0) {
pr_err("fail get osc ring efuse info\n");
return 0;
}
if (is_meson_sm1_cpu()) {
for (i = 1; i <= 4; i++)
ret += sprintf(str + ret, "%s:%d KHz, ", name[i], (ringinfo[i] * 20)); //cpu_ring0,mail_ring1,dmc_ring,cpu_ring2
ret += sprintf(str + ret, "%s:%d uA, ", name[i], (ringinfo[i] * 400)); //iddee
i += 2;
ret += sprintf(str + ret, "%s:%d uA, ", name[i], (ringinfo[i] * 400)); //iddcpu
} else
pr_err("show iddcpu: not support non-sm1 chip!\n");
ret = sprintf(buf, "%s\n", str);
return ret;
}
static struct class_attribute dvfsid_class_attrs[] = {
__ATTR(dvfs_id, 0444, show_dvfsid, NULL),
__ATTR(ringmsr, 0444, show_ringmsr, NULL),
__ATTR_NULL
};
static int dvfsid_probe(struct platform_device *pdev)
{
dvfs_id = get_cpufreq_table_index(GET_DVFS_TABLE_INDEX, 0, 0, 0);
pr_info("%s id:%d\n", __func__, dvfs_id);
dvfsid_cls = kzalloc(sizeof(struct class), GFP_KERNEL);
if (dvfsid_cls == NULL) {
pr_err("%s no mem for zalloc\n", __func__);
return -1;
}
dvfsid_cls->name = dvfsid_dev;
dvfsid_cls->class_attrs = dvfsid_class_attrs;
if (class_register(dvfsid_cls) < 0) {
pr_err("failed to class_reg for dvfsid\n");
return -1;
}
return 0;
}
static const struct of_device_id dvfsid_dt_match[] = {
{ .compatible = "amlogic, dvfs-id" },
{ /* sentinel */ },
};
static struct platform_driver dvfsid_platform_driver = {
.probe = dvfsid_probe,
.driver = {
.owner = THIS_MODULE,
.name = "dvfs-id",
.of_match_table = dvfsid_dt_match,
},
};
static int __init meson_dvfsid_init(void)
{
return platform_driver_register(&dvfsid_platform_driver);
}
static void __exit meson_dvfsid_exit(void)
{
kfree(dvfsid_cls);
platform_driver_unregister(&dvfsid_platform_driver);
}
module_init(meson_dvfsid_init);
module_exit(meson_dvfsid_exit);