Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,7 @@
# Exynos DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_DEVFREQ) += exynos-devfreq.o
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o
obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o
obj-$(CONFIG_ARM_EXYNOS8890_BUS_DEVFREQ)+= exynos8890_bus_mif.o exynos8890_bus_int.o exynos8890_bus_disp.o exynos8890_bus_cam.o
obj-$(CONFIG_ARM_EXYNOS7870_BUS_DEVFREQ)+= exynos7870_bus_mif.o exynos7870_bus_int.o exynos7870_bus_disp.o exynos7870_bus_cam.o
obj-$(CONFIG_ARM_EXYNOS7570_BUS_DEVFREQ)+= exynos7570_bus_mif.o exynos7570_bus_int.o exynos7570_bus_disp.o exynos7570_bus_cam.o

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS4 BUS header
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DEVFREQ_EXYNOS4_BUS_H
#define __DEVFREQ_EXYNOS4_BUS_H __FILE__
#include <mach/map.h>
#define EXYNOS4_CLKDIV_LEFTBUS (S5P_VA_CMU + 0x04500)
#define EXYNOS4_CLKDIV_STAT_LEFTBUS (S5P_VA_CMU + 0x04600)
#define EXYNOS4_CLKDIV_RIGHTBUS (S5P_VA_CMU + 0x08500)
#define EXYNOS4_CLKDIV_STAT_RIGHTBUS (S5P_VA_CMU + 0x08600)
#define EXYNOS4_CLKDIV_TOP (S5P_VA_CMU + 0x0C510)
#define EXYNOS4_CLKDIV_CAM (S5P_VA_CMU + 0x0C520)
#define EXYNOS4_CLKDIV_MFC (S5P_VA_CMU + 0x0C528)
#define EXYNOS4_CLKDIV_STAT_TOP (S5P_VA_CMU + 0x0C610)
#define EXYNOS4_CLKDIV_STAT_MFC (S5P_VA_CMU + 0x0C628)
#define EXYNOS4210_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x0C930)
#define EXYNOS4212_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x04930)
#define EXYNOS4_CLKDIV_DMC0 (S5P_VA_CMU + 0x10500)
#define EXYNOS4_CLKDIV_DMC1 (S5P_VA_CMU + 0x10504)
#define EXYNOS4_CLKDIV_STAT_DMC0 (S5P_VA_CMU + 0x10600)
#define EXYNOS4_CLKDIV_STAT_DMC1 (S5P_VA_CMU + 0x10604)
#define EXYNOS4_DMC_PAUSE_CTRL (S5P_VA_CMU + 0x11094)
#define EXYNOS4_DMC_PAUSE_ENABLE (1 << 0)
#define EXYNOS4_CLKDIV_DMC0_ACP_SHIFT (0)
#define EXYNOS4_CLKDIV_DMC0_ACP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT (4)
#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT (8)
#define EXYNOS4_CLKDIV_DMC0_DPHY_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_DMC_SHIFT (12)
#define EXYNOS4_CLKDIV_DMC0_DMC_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT (16)
#define EXYNOS4_CLKDIV_DMC0_DMCD_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT (20)
#define EXYNOS4_CLKDIV_DMC0_DMCP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT (24)
#define EXYNOS4_CLKDIV_DMC0_COPY2_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT)
#define EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT (28)
#define EXYNOS4_CLKDIV_DMC0_CORETI_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT)
#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT (0)
#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK (0xf << EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT)
#define EXYNOS4_CLKDIV_DMC1_C2C_SHIFT (4)
#define EXYNOS4_CLKDIV_DMC1_C2C_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2C_SHIFT)
#define EXYNOS4_CLKDIV_DMC1_PWI_SHIFT (8)
#define EXYNOS4_CLKDIV_DMC1_PWI_MASK (0xf << EXYNOS4_CLKDIV_DMC1_PWI_SHIFT)
#define EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT (12)
#define EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT)
#define EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT (16)
#define EXYNOS4_CLKDIV_DMC1_DVSEM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT)
#define EXYNOS4_CLKDIV_DMC1_DPM_SHIFT (24)
#define EXYNOS4_CLKDIV_DMC1_DPM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DPM_SHIFT)
#define EXYNOS4_CLKDIV_MFC_SHIFT (0)
#define EXYNOS4_CLKDIV_MFC_MASK (0x7 << EXYNOS4_CLKDIV_MFC_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT (0)
#define EXYNOS4_CLKDIV_TOP_ACLK200_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT (4)
#define EXYNOS4_CLKDIV_TOP_ACLK100_MASK (0xF << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT (8)
#define EXYNOS4_CLKDIV_TOP_ACLK160_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT (12)
#define EXYNOS4_CLKDIV_TOP_ACLK133_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT (16)
#define EXYNOS4_CLKDIV_TOP_ONENAND_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT (20)
#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT)
#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT (24)
#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT)
#define EXYNOS4_CLKDIV_BUS_GDLR_SHIFT (0)
#define EXYNOS4_CLKDIV_BUS_GDLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT)
#define EXYNOS4_CLKDIV_BUS_GPLR_SHIFT (4)
#define EXYNOS4_CLKDIV_BUS_GPLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)
#define EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT (0)
#define EXYNOS4_CLKDIV_CAM_FIMC0_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT)
#define EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT (4)
#define EXYNOS4_CLKDIV_CAM_FIMC1_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT)
#define EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT (8)
#define EXYNOS4_CLKDIV_CAM_FIMC2_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT)
#define EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT (12)
#define EXYNOS4_CLKDIV_CAM_FIMC3_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT)
#define EXYNOS4_CLKDIV_CAM1 (S5P_VA_CMU + 0x0C568)
#define EXYNOS4_CLKDIV_STAT_CAM1 (S5P_VA_CMU + 0x0C668)
#define EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT (0)
#define EXYNOS4_CLKDIV_CAM1_JPEG_MASK (0xf << EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT)
#endif /* __DEVFREQ_EXYNOS4_BUS_H */

View file

@ -0,0 +1,432 @@
/*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS5 INT clock frequency scaling support using DEVFREQ framework
* Based on work done by Jonghwan Choi <jhbird.choi@samsung.com>
* Support for only EXYNOS5250 is present.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/regulator/consumer.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include "exynos_ppmu.h"
#define MAX_SAFEVOLT 1100000 /* 1.10V */
/* Assume that the bus is saturated if the utilization is 25% */
#define INT_BUS_SATURATION_RATIO 25
enum int_level_idx {
LV_0,
LV_1,
LV_2,
LV_3,
LV_4,
_LV_END
};
enum exynos_ppmu_list {
PPMU_RIGHT,
PPMU_END,
};
struct busfreq_data_int {
struct device *dev;
struct devfreq *devfreq;
struct regulator *vdd_int;
struct busfreq_ppmu_data ppmu_data;
unsigned long curr_freq;
bool disabled;
struct notifier_block pm_notifier;
struct mutex lock;
struct pm_qos_request int_req;
struct clk *int_clk;
};
struct int_bus_opp_table {
unsigned int idx;
unsigned long clk;
unsigned long volt;
};
static struct int_bus_opp_table exynos5_int_opp_table[] = {
{LV_0, 266000, 1025000},
{LV_1, 200000, 1025000},
{LV_2, 160000, 1025000},
{LV_3, 133000, 1025000},
{LV_4, 100000, 1025000},
{0, 0, 0},
};
static int exynos5_int_setvolt(struct busfreq_data_int *data,
unsigned long volt)
{
return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT);
}
static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
u32 flags)
{
int err = 0;
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct busfreq_data_int *data = platform_get_drvdata(pdev);
struct dev_pm_opp *opp;
unsigned long old_freq, freq;
unsigned long volt;
rcu_read_lock();
opp = devfreq_recommended_opp(dev, _freq, flags);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(dev, "%s: Invalid OPP.\n", __func__);
return PTR_ERR(opp);
}
freq = dev_pm_opp_get_freq(opp);
volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
old_freq = data->curr_freq;
if (old_freq == freq)
return 0;
dev_dbg(dev, "targeting %lukHz %luuV\n", freq, volt);
mutex_lock(&data->lock);
if (data->disabled)
goto out;
if (freq > exynos5_int_opp_table[0].clk)
pm_qos_update_request(&data->int_req, freq * 16 / 1000);
else
pm_qos_update_request(&data->int_req, -1);
if (old_freq < freq)
err = exynos5_int_setvolt(data, volt);
if (err)
goto out;
err = clk_set_rate(data->int_clk, freq * 1000);
if (err)
goto out;
if (old_freq > freq)
err = exynos5_int_setvolt(data, volt);
if (err)
goto out;
data->curr_freq = freq;
out:
mutex_unlock(&data->lock);
return err;
}
static int exynos5_int_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct busfreq_data_int *data = platform_get_drvdata(pdev);
struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
int busier_dmc;
exynos_read_ppmu(ppmu_data);
busier_dmc = exynos_get_busier_ppmu(ppmu_data);
stat->current_frequency = data->curr_freq;
/* Number of cycles spent on memory access */
stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3];
stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO;
stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt;
return 0;
}
static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
.initial_freq = 160000,
.polling_ms = 100,
.target = exynos5_busfreq_int_target,
.get_dev_status = exynos5_int_get_dev_status,
};
static int exynos5250_init_int_tables(struct busfreq_data_int *data)
{
int i, err = 0;
for (i = LV_0; i < _LV_END; i++) {
err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,
exynos5_int_opp_table[i].volt);
if (err) {
dev_err(data->dev, "Cannot add opp entries.\n");
return err;
}
}
return 0;
}
static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct busfreq_data_int *data = container_of(this,
struct busfreq_data_int, pm_notifier);
struct dev_pm_opp *opp;
unsigned long maxfreq = ULONG_MAX;
unsigned long freq;
unsigned long volt;
int err = 0;
switch (event) {
case PM_SUSPEND_PREPARE:
/* Set Fastest and Deactivate DVFS */
mutex_lock(&data->lock);
data->disabled = true;
rcu_read_lock();
opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq);
if (IS_ERR(opp)) {
rcu_read_unlock();
err = PTR_ERR(opp);
goto unlock;
}
freq = dev_pm_opp_get_freq(opp);
volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
err = exynos5_int_setvolt(data, volt);
if (err)
goto unlock;
err = clk_set_rate(data->int_clk, freq * 1000);
if (err)
goto unlock;
data->curr_freq = freq;
unlock:
mutex_unlock(&data->lock);
if (err)
return NOTIFY_BAD;
return NOTIFY_OK;
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
/* Reactivate */
mutex_lock(&data->lock);
data->disabled = false;
mutex_unlock(&data->lock);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static int exynos5_busfreq_int_probe(struct platform_device *pdev)
{
struct busfreq_data_int *data;
struct busfreq_ppmu_data *ppmu_data;
struct dev_pm_opp *opp;
struct device *dev = &pdev->dev;
struct device_node *np;
unsigned long initial_freq;
unsigned long initial_volt;
int err = 0;
int i;
data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int),
GFP_KERNEL);
if (data == NULL) {
dev_err(dev, "Cannot allocate memory.\n");
return -ENOMEM;
}
ppmu_data = &data->ppmu_data;
ppmu_data->ppmu_end = PPMU_END;
ppmu_data->ppmu = devm_kzalloc(dev,
sizeof(struct exynos_ppmu) * PPMU_END,
GFP_KERNEL);
if (!ppmu_data->ppmu) {
dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
return -ENOMEM;
}
np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
if (np == NULL) {
pr_err("Unable to find PPMU node\n");
return -ENOENT;
}
for (i = 0; i < ppmu_data->ppmu_end; i++) {
/* map PPMU memory region */
ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
if (ppmu_data->ppmu[i].hw_base == NULL) {
dev_err(&pdev->dev, "failed to map memory region\n");
return -ENOMEM;
}
}
data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event;
data->dev = dev;
mutex_init(&data->lock);
err = exynos5250_init_int_tables(data);
if (err)
return err;
data->vdd_int = devm_regulator_get(dev, "vdd_int");
if (IS_ERR(data->vdd_int)) {
dev_err(dev, "Cannot get the regulator \"vdd_int\"\n");
return PTR_ERR(data->vdd_int);
}
data->int_clk = devm_clk_get(dev, "int_clk");
if (IS_ERR(data->int_clk)) {
dev_err(dev, "Cannot get clock \"int_clk\"\n");
return PTR_ERR(data->int_clk);
}
rcu_read_lock();
opp = dev_pm_opp_find_freq_floor(dev,
&exynos5_devfreq_int_profile.initial_freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(dev, "Invalid initial frequency %lu kHz.\n",
exynos5_devfreq_int_profile.initial_freq);
return PTR_ERR(opp);
}
initial_freq = dev_pm_opp_get_freq(opp);
initial_volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
data->curr_freq = initial_freq;
err = clk_set_rate(data->int_clk, initial_freq * 1000);
if (err) {
dev_err(dev, "Failed to set initial frequency\n");
return err;
}
err = exynos5_int_setvolt(data, initial_volt);
if (err)
return err;
platform_set_drvdata(pdev, data);
busfreq_mon_reset(ppmu_data);
data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile,
"simple_ondemand", NULL);
if (IS_ERR(data->devfreq))
return PTR_ERR(data->devfreq);
err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
if (err < 0) {
dev_err(dev, "Failed to register opp notifier\n");
return err;
}
err = register_pm_notifier(&data->pm_notifier);
if (err) {
dev_err(dev, "Failed to setup pm notifier\n");
return err;
}
/* TODO: Add a new QOS class for int/mif bus */
pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);
return 0;
}
static int exynos5_busfreq_int_remove(struct platform_device *pdev)
{
struct busfreq_data_int *data = platform_get_drvdata(pdev);
pm_qos_remove_request(&data->int_req);
unregister_pm_notifier(&data->pm_notifier);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int exynos5_busfreq_int_resume(struct device *dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct busfreq_data_int *data = platform_get_drvdata(pdev);
struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
busfreq_mon_reset(ppmu_data);
return 0;
}
static const struct dev_pm_ops exynos5_busfreq_int_pm = {
.resume = exynos5_busfreq_int_resume,
};
#endif
static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL,
exynos5_busfreq_int_resume);
/* platform device pointer for exynos5 devfreq device. */
static struct platform_device *exynos5_devfreq_pdev;
static struct platform_driver exynos5_busfreq_int_driver = {
.probe = exynos5_busfreq_int_probe,
.remove = exynos5_busfreq_int_remove,
.driver = {
.name = "exynos5-bus-int",
.owner = THIS_MODULE,
.pm = &exynos5_busfreq_int_pm_ops,
},
};
static int __init exynos5_busfreq_int_init(void)
{
int ret;
ret = platform_driver_register(&exynos5_busfreq_int_driver);
if (ret < 0)
goto out;
exynos5_devfreq_pdev =
platform_device_register_simple("exynos5-bus-int", -1, NULL, 0);
if (IS_ERR(exynos5_devfreq_pdev)) {
ret = PTR_ERR(exynos5_devfreq_pdev);
goto out1;
}
return 0;
out1:
platform_driver_unregister(&exynos5_busfreq_int_driver);
out:
return ret;
}
late_initcall(exynos5_busfreq_int_init);
static void __exit exynos5_busfreq_int_exit(void)
{
platform_device_unregister(exynos5_devfreq_pdev);
platform_driver_unregister(&exynos5_busfreq_int_driver);
}
module_exit(exynos5_busfreq_int_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EXYNOS5 busfreq driver with devfreq framework");

View file

@ -0,0 +1,209 @@
/* linux/drivers/devfreq/exynos/exynos7570_bus_cam.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7570 SoC CAM devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7570/S5E7570-vclk.h"
#include "../governor.h"
static int exynos7570_devfreq_cam_cmu_dump(struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_cam);
return 0;
}
static int exynos7570_devfreq_cam_reboot(struct exynos_devfreq_data *data)
{
data->max_freq = data->reboot_freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos7570_devfreq_cam_get_freq(struct device *dev, u32 *cur_freq, struct clk *clk)
{
*cur_freq = (u32)clk_get_rate(clk);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_cam_set_freq(struct device *dev, u32 new_freq, struct clk *clk)
{
if (clk_set_rate(clk, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n", new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_cam_init_freq_table(struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_cam);
if (!max_freq) {
dev_err(data->dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(data->dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_cam);
if (!min_freq) {
dev_err(data->dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(data->dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(data->dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(data->dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7570_devfreq_cam_get_volt_table(struct device *dev, u32 max_state,
struct exynos_devfreq_opp_table *opp_list)
{
struct dvfs_rate_volt cam_rate_volt[max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_cam, cam_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < max_state; i++) {
if (opp_list[i].freq != (u32)(cam_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
opp_list[i].freq, (u32)cam_rate_volt[i].rate);
return -EINVAL;
}
opp_list[i].volt= (u32)cam_rate_volt[i].volt;
}
return 0;
}
static int exynos7570_devfreq_cam_init(struct exynos_devfreq_data *data)
{
data->clk = clk_get(data->dev, "dvfs_cam");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(data->dev, "failed get dvfs vclk\n");
return -ENODEV;
}
return 0;
}
static int exynos7570_devfreq_cam_exit(struct exynos_devfreq_data *data)
{
clk_put(data->clk);
return 0;
}
static int __init exynos7570_devfreq_cam_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7570_devfreq_cam_init;
data->ops.exit = exynos7570_devfreq_cam_exit;
data->ops.get_volt_table = exynos7570_devfreq_cam_get_volt_table;
data->ops.get_freq = exynos7570_devfreq_cam_get_freq;
data->ops.set_freq = exynos7570_devfreq_cam_set_freq;
data->ops.init_freq_table = exynos7570_devfreq_cam_init_freq_table;
data->ops.reboot = exynos7570_devfreq_cam_reboot;
data->ops.cmu_dump = exynos7570_devfreq_cam_cmu_dump;
return 0;
}
static int __init exynos7570_devfreq_cam_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_CAM,
exynos7570_devfreq_cam_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7570_devfreq_cam_initcall);

View file

@ -0,0 +1,209 @@
/* linux/drivers/devfreq/exynos/exynos7570_bus_disp.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7570 SoC DISP devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7570/S5E7570-vclk.h"
#include "../governor.h"
static int exynos7570_devfreq_disp_cmu_dump(struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_disp);
return 0;
}
static int exynos7570_devfreq_disp_reboot(struct exynos_devfreq_data *data)
{
data->max_freq = data->reboot_freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos7570_devfreq_disp_get_freq(struct device *dev, u32 *cur_freq, struct clk *clk)
{
*cur_freq = (u32)clk_get_rate(clk);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_disp_set_freq(struct device *dev, u32 new_freq, struct clk *clk)
{
if (clk_set_rate(clk, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n", new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_disp_init_freq_table(struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_disp);
if (!max_freq) {
dev_err(data->dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(data->dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_disp);
if (!min_freq) {
dev_err(data->dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(data->dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(data->dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(data->dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7570_devfreq_disp_get_volt_table(struct device *dev, u32 max_state,
struct exynos_devfreq_opp_table *opp_list)
{
struct dvfs_rate_volt disp_rate_volt[max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_disp, disp_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < max_state; i++) {
if (opp_list[i].freq != (u32)(disp_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
opp_list[i].freq, (u32)disp_rate_volt[i].rate);
return -EINVAL;
}
opp_list[i].volt= (u32)disp_rate_volt[i].volt;
}
return 0;
}
static int exynos7570_devfreq_disp_init(struct exynos_devfreq_data *data)
{
data->clk = clk_get(data->dev, "dvfs_disp");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(data->dev, "failed get dvfs vclk\n");
return -ENODEV;
}
return 0;
}
static int exynos7570_devfreq_disp_exit(struct exynos_devfreq_data *data)
{
clk_put(data->clk);
return 0;
}
static int __init exynos7570_devfreq_disp_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7570_devfreq_disp_init;
data->ops.exit = exynos7570_devfreq_disp_exit;
data->ops.get_volt_table = exynos7570_devfreq_disp_get_volt_table;
data->ops.get_freq = exynos7570_devfreq_disp_get_freq;
data->ops.set_freq = exynos7570_devfreq_disp_set_freq;
data->ops.init_freq_table = exynos7570_devfreq_disp_init_freq_table;
data->ops.reboot = exynos7570_devfreq_disp_reboot;
data->ops.cmu_dump = exynos7570_devfreq_disp_cmu_dump;
return 0;
}
static int __init exynos7570_devfreq_disp_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_DISP,
exynos7570_devfreq_disp_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7570_devfreq_disp_initcall);

View file

@ -0,0 +1,209 @@
/* linux/drivers/devfreq/exynos/exynos7570_bus_int.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7570 SoC INT devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7570/S5E7570-vclk.h"
#include "../governor.h"
static int exynos7570_devfreq_int_cmu_dump(struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_int);
return 0;
}
static int exynos7570_devfreq_int_reboot(struct exynos_devfreq_data *data)
{
data->max_freq = data->reboot_freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos7570_devfreq_int_get_freq(struct device *dev, u32 *cur_freq, struct clk *clk)
{
*cur_freq = (u32)clk_get_rate(clk);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_int_set_freq(struct device *dev, u32 new_freq, struct clk *clk)
{
if (clk_set_rate(clk, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n", new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_int_init_freq_table(struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_int);
if (!max_freq) {
dev_err(data->dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(data->dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_int);
if (!min_freq) {
dev_err(data->dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(data->dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(data->dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(data->dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7570_devfreq_int_get_volt_table(struct device *dev, u32 max_state,
struct exynos_devfreq_opp_table *opp_list)
{
struct dvfs_rate_volt int_rate_volt[max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_int, int_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < max_state; i++) {
if (opp_list[i].freq != (u32)(int_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
opp_list[i].freq, (u32)int_rate_volt[i].rate);
return -EINVAL;
}
opp_list[i].volt= (u32)int_rate_volt[i].volt;
}
return 0;
}
static int exynos7570_devfreq_int_init(struct exynos_devfreq_data *data)
{
data->clk = clk_get(data->dev, "dvfs_int");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(data->dev, "failed get dvfs vclk\n");
return -ENODEV;
}
return 0;
}
static int exynos7570_devfreq_int_exit(struct exynos_devfreq_data *data)
{
clk_put(data->clk);
return 0;
}
static int __init exynos7570_devfreq_int_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7570_devfreq_int_init;
data->ops.exit = exynos7570_devfreq_int_exit;
data->ops.get_volt_table = exynos7570_devfreq_int_get_volt_table;
data->ops.get_freq = exynos7570_devfreq_int_get_freq;
data->ops.set_freq = exynos7570_devfreq_int_set_freq;
data->ops.init_freq_table = exynos7570_devfreq_int_init_freq_table;
data->ops.reboot = exynos7570_devfreq_int_reboot;
data->ops.cmu_dump = exynos7570_devfreq_int_cmu_dump;
return 0;
}
static int __init exynos7570_devfreq_int_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_INT,
exynos7570_devfreq_int_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7570_devfreq_int_initcall);

View file

@ -0,0 +1,319 @@
/* linux/drivers/devfreq/exynos/exynos7570_bus_mif.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7570 SoC MIF devfreq driver
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/workqueue.h>
#ifdef CONFIG_UMTS_MODEM_SS310AP
#include <linux/exynos-modem-ctrl.h>
#endif
#include <soc/samsung/exynos-devfreq.h>
#include <soc/samsung/bts.h>
#include <linux/apm-exynos.h>
#include <soc/samsung/asv-exynos.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7570/S5E7570-vclk.h"
#include "../governor.h"
#ifdef CONFIG_SHARE_MIF_FREQ_INFO
#include <linux/shm_ipc.h>
#endif
#ifdef CONFIG_SOC_EXYNOS7570_DUAL
#define DEVFREQ_MIF_SWITCH_FREQ (840000)
#define DEVFREQ_MIF_EVS_FREQ (676000)
#else
#define DEVFREQ_MIF_SWITCH_FREQ (830000)
#define DEVFREQ_MIF_EVS_FREQ (666000)
#endif
static unsigned long origin_suspend_freq = 0;
u32 sw_volt_table;
int is_dll_on(void)
{
return cal_dfs_ext_ctrl(dvfs_mif, cal_dfs_mif_is_dll_on, 0);
}
EXPORT_SYMBOL_GPL(is_dll_on);
static int exynos7570_devfreq_mif_set_freq_post(struct exynos_devfreq_data *data)
{
#ifdef CONFIG_SHARE_MIF_FREQ_INFO
shm_set_mif_freq(data->new_freq);
#endif
return 0;
}
static int exynos7570_devfreq_mif_cmu_dump(struct exynos_devfreq_data *data)
{
mutex_lock(&data->devfreq->lock);
cal_vclk_dbg_info(dvfs_mif);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos7570_devfreq_mif_pm_suspend_prepare(struct exynos_devfreq_data *data)
{
if (!origin_suspend_freq)
origin_suspend_freq = data->devfreq_profile.suspend_freq;
#ifdef CONFIG_UMTS_MODEM_SS310AP
if (ss310ap_get_evs_mode_ext())
data->devfreq_profile.suspend_freq = DEVFREQ_MIF_EVS_FREQ;
else
data->devfreq_profile.suspend_freq = origin_suspend_freq;
#endif
return 0;
}
static int exynos7570_devfreq_cl_dvfs_start(struct device *dev)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_CL_DVFS_MIF
ret = exynos_cl_dvfs_start(ID_MIF);
#endif
return ret;
}
static int exynos7570_devfreq_cl_dvfs_stop(struct device *dev, u32 target_idx)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_CL_DVFS_MIF
ret = exynos_cl_dvfs_stop(ID_MIF, target_idx);
#endif
return ret;
}
static int exynos7570_devfreq_mif_get_switch_freq(struct device *dev, u32 cur_freq,
u32 new_freq, u32 *switch_freq)
{
*switch_freq = DEVFREQ_MIF_SWITCH_FREQ;
return 0;
}
static int exynos7570_devfreq_mif_get_switch_voltage(struct device *dev, u32 cur_freq,
u32 new_freq, u32 cur_volt, u32 new_volt, u32 *switch_volt)
{
*switch_volt = sw_volt_table;
return 0;
}
static int exynos7570_devfreq_mif_get_freq(struct device *dev, u32 *cur_freq, struct clk *clk)
{
*cur_freq = (u32)clk_get_rate(clk);
if (*cur_freq == 0) {
dev_err(dev, "failed get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_mif_restore_from_switch_freq(struct device *dev,
struct clk *clk, u32 cur_freq, u32 new_freq)
{
if (clk_set_rate(clk, new_freq)) {
dev_err(dev, "failed to set frequency by CAL (%uKhz)\n", new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7570_devfreq_mif_init_freq_table(struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq, cur_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i, ret;
ret = cal_clk_enable(dvfs_mif);
if (ret) {
dev_err(data->dev, "failed to enable MIF\n");
return -EINVAL;
}
max_freq = (u32)cal_dfs_get_max_freq(dvfs_mif);
if (!max_freq) {
dev_err(data->dev, "failed get max frequency\n");
return -EINVAL;
}
dev_info(data->dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_mif);
if (!min_freq) {
dev_err(data->dev, "failed get min frequency\n");
return -EINVAL;
}
dev_info(data->dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(data->dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(data->dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(data->dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
cur_freq = clk_get_rate(data->clk);
dev_info(data->dev, "current frequency: %uKhz\n", cur_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(data->dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7570_devfreq_mif_get_volt_table(struct device *dev, u32 max_state,
struct exynos_devfreq_opp_table *opp_list)
{
struct dvfs_rate_volt mif_rate_volt[max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_mif, mif_rate_volt);
if (!table_size) {
dev_err(dev, "failed get ASV table\n");
return -ENODEV;
}
if (table_size != max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < max_state; i++) {
if (opp_list[i].freq != (u32)(mif_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
opp_list[i].freq, (u32)mif_rate_volt[i].rate);
return -EINVAL;
}
opp_list[i].volt = (u32)mif_rate_volt[i].volt;
/* Fill switch voltage table */
}
if (!sw_volt_table)
sw_volt_table = (u32)mif_rate_volt[0].volt;
dev_info(dev, "SW_volt %uuV in freq %uKhz\n",
sw_volt_table, DEVFREQ_MIF_SWITCH_FREQ);
return 0;
}
static int exynos7570_devfreq_mif_init(struct exynos_devfreq_data *data)
{
data->clk = clk_get(data->dev, "dvfs_mif");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(data->dev, "failed get dvfs vclk\n");
return -ENODEV;
}
data->sw_clk = clk_get(data->dev, "dvfs_mif_sw");
if (IS_ERR_OR_NULL(data->sw_clk)) {
dev_err(data->dev, "failed get dvfs sw vclk\n");
clk_put(data->clk);
return -ENODEV;
}
return 0;
}
static int exynos7570_devfreq_mif_exit(struct exynos_devfreq_data *data)
{
clk_put(data->sw_clk);
clk_put(data->clk);
return 0;
}
static int __init exynos7570_devfreq_mif_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7570_devfreq_mif_init;
data->ops.exit = exynos7570_devfreq_mif_exit;
data->ops.get_volt_table = exynos7570_devfreq_mif_get_volt_table;
data->ops.get_switch_freq = exynos7570_devfreq_mif_get_switch_freq;
data->ops.get_switch_voltage = exynos7570_devfreq_mif_get_switch_voltage;
data->ops.get_freq = exynos7570_devfreq_mif_get_freq;
data->ops.restore_from_switch_freq = exynos7570_devfreq_mif_restore_from_switch_freq;
data->ops.init_freq_table = exynos7570_devfreq_mif_init_freq_table;
data->ops.cl_dvfs_start = exynos7570_devfreq_cl_dvfs_start;
data->ops.cl_dvfs_stop = exynos7570_devfreq_cl_dvfs_stop;
data->ops.pm_suspend_prepare = exynos7570_devfreq_mif_pm_suspend_prepare;
data->ops.cmu_dump = exynos7570_devfreq_mif_cmu_dump;
data->ops.set_freq_post = exynos7570_devfreq_mif_set_freq_post;
return 0;
}
static int __init exynos7570_devfreq_mif_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_MIF,
exynos7570_devfreq_mif_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7570_devfreq_mif_initcall);

View file

@ -0,0 +1,231 @@
/* linux/drivers/devfreq/exynos/exynos7870_bus_cam.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7870 SoC CAM devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7870/S5E7870-vclk.h"
#include "../governor.h"
#define DEVFREQ_CAM_REBOOT_FREQ (400000)
static struct exynos_devfreq_data *cam_data;
static int exynos7870_devfreq_cam_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_cam);
return 0;
}
static int exynos7870_devfreq_cam_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_CAM_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static u32 exynos7870_devfreq_cam_get_target_freq(char *name, u32 freq)
{
return cal_dfs_get_rate_by_member(dvfs_cam, name, freq);
}
static int exynos7870_devfreq_cam_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)clk_get_rate(data->clk);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_cam_set_freq(struct device *dev,
u32 old_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (clk_set_rate(data->clk, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n",
new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_cam_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_cam);
if (!max_freq) {
dev_err(dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_cam);
if (!min_freq) {
dev_err(dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7870_devfreq_cam_get_volt_table(struct device *dev,
u32 *volt_table, struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt cam_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_cam, cam_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(cam_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)cam_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)cam_rate_volt[i].volt;
}
return 0;
}
static int exynos7870_devfreq_cam_init(struct device *dev,
struct exynos_devfreq_data *data)
{
data->clk = clk_get(dev, "dvfs_cam");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(dev, "failed get dvfs vclk\n");
return -ENODEV;
}
return 0;
}
static int exynos7870_devfreq_cam_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
clk_put(data->clk);
return 0;
}
static int __init exynos7870_devfreq_cam_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7870_devfreq_cam_init;
data->ops.exit = exynos7870_devfreq_cam_exit;
data->ops.get_volt_table = exynos7870_devfreq_cam_get_volt_table;
data->ops.get_freq = exynos7870_devfreq_cam_get_freq;
data->ops.set_freq = exynos7870_devfreq_cam_set_freq;
data->ops.init_freq_table = exynos7870_devfreq_cam_init_freq_table;
data->ops.get_target_freq = exynos7870_devfreq_cam_get_target_freq;
data->ops.reboot = exynos7870_devfreq_cam_reboot;
data->ops.cmu_dump = exynos7870_devfreq_cam_cmu_dump;
cam_data = data;
return 0;
}
static int __init exynos7870_devfreq_cam_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_CAM,
exynos7870_devfreq_cam_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7870_devfreq_cam_initcall);

View file

@ -0,0 +1,231 @@
/* linux/drivers/devfreq/exynos/exynos7870_bus_disp.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7870 SoC DISP devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7870/S5E7870-vclk.h"
#include "../governor.h"
#define DEVFREQ_DISP_REBOOT_FREQ (200000)
static struct exynos_devfreq_data *disp_data;
static int exynos7870_devfreq_disp_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_disp);
return 0;
}
static int exynos7870_devfreq_disp_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_DISP_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static u32 exynos7870_devfreq_disp_get_target_freq(char *name, u32 freq)
{
return cal_dfs_get_rate_by_member(dvfs_disp, name, freq);
}
static int exynos7870_devfreq_disp_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)clk_get_rate(data->clk);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_disp_set_freq(struct device *dev,
u32 old_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (clk_set_rate(data->clk, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n",
new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_disp_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_disp);
if (!max_freq) {
dev_err(dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_disp);
if (!min_freq) {
dev_err(dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7870_devfreq_disp_get_volt_table(struct device *dev,
u32 *volt_table, struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt disp_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_disp, disp_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(disp_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)disp_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)disp_rate_volt[i].volt;
}
return 0;
}
static int exynos7870_devfreq_disp_init(struct device *dev,
struct exynos_devfreq_data *data)
{
data->clk = clk_get(dev, "dvfs_disp");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(dev, "failed get dvfs vclk\n");
return -ENODEV;
}
return 0;
}
static int exynos7870_devfreq_disp_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
clk_put(data->clk);
return 0;
}
static int __init exynos7870_devfreq_disp_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7870_devfreq_disp_init;
data->ops.exit = exynos7870_devfreq_disp_exit;
data->ops.get_volt_table = exynos7870_devfreq_disp_get_volt_table;
data->ops.get_freq = exynos7870_devfreq_disp_get_freq;
data->ops.set_freq = exynos7870_devfreq_disp_set_freq;
data->ops.init_freq_table = exynos7870_devfreq_disp_init_freq_table;
data->ops.get_target_freq = exynos7870_devfreq_disp_get_target_freq;
data->ops.reboot = exynos7870_devfreq_disp_reboot;
data->ops.cmu_dump = exynos7870_devfreq_disp_cmu_dump;
disp_data = data;
return 0;
}
static int __init exynos7870_devfreq_disp_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_DISP,
exynos7870_devfreq_disp_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7870_devfreq_disp_initcall);

View file

@ -0,0 +1,232 @@
/* linux/drivers/devfreq/exynos/exynos7870_bus_int.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7870 SoC INT devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7870/S5E7870-vclk.h"
#include "../governor.h"
#define DEVFREQ_INT_REBOOT_FREQ (400000)
static struct exynos_devfreq_data *int_data;
static int exynos7870_devfreq_int_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_int);
return 0;
}
static int exynos7870_devfreq_int_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_INT_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static u32 exynos7870_devfreq_int_get_target_freq(char *name, u32 freq)
{
return cal_dfs_get_rate_by_member(dvfs_int, name, freq);
}
static int exynos7870_devfreq_int_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)clk_get_rate(data->clk);
if (*cur_freq == 0) {
dev_err(dev, "failed get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_int_set_freq(struct device *dev,
u32 old_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (clk_set_rate(data->clk, (unsigned long)new_freq)) {
dev_err(dev, "failed set frequency to CAL (%uKhz)\n",
new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_int_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_int);
if (!max_freq) {
dev_err(dev, "failed get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_int);
if (!min_freq) {
dev_err(dev, "failed get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7870_devfreq_int_get_volt_table(struct device *dev, u32 *volt_table,
struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt int_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_int, int_rate_volt);
if (!table_size) {
dev_err(dev, "failed get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(int_rate_volt[i].rate)) {
dev_err(dev, "Freq tablle is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)int_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)int_rate_volt[i].volt;
}
return 0;
}
static int exynos7870_devfreq_int_init(struct device *dev,
struct exynos_devfreq_data *data)
{
data->clk = clk_get(dev, "dvfs_int");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(dev, "failed get dvfs vclk\n");
return -ENODEV;
}
return 0;
}
static int exynos7870_devfreq_int_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
clk_put(data->clk);
return 0;
}
static int __init exynos7870_devfreq_int_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7870_devfreq_int_init;
data->ops.exit = exynos7870_devfreq_int_exit;
data->ops.get_volt_table = exynos7870_devfreq_int_get_volt_table;
data->ops.get_freq = exynos7870_devfreq_int_get_freq;
data->ops.set_freq = exynos7870_devfreq_int_set_freq;
data->ops.init_freq_table = exynos7870_devfreq_int_init_freq_table;
data->ops.get_target_freq = exynos7870_devfreq_int_get_target_freq;
data->ops.reboot = exynos7870_devfreq_int_reboot;
data->ops.cmu_dump = exynos7870_devfreq_int_cmu_dump;
int_data = data;
return 0;
}
static int __init exynos7870_devfreq_int_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_INT,
exynos7870_devfreq_int_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7870_devfreq_int_initcall);

View file

@ -0,0 +1,377 @@
/* linux/drivers/devfreq/exynos/exynos7870_bus_mif.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS7870 SoC MIF devfreq driver
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <soc/samsung/exynos-devfreq.h>
#include <soc/samsung/bts.h>
#include <linux/apm-exynos.h>
#include <soc/samsung/asv-exynos.h>
#include <linux/mcu_ipc.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E7870/S5E7870-vclk.h"
#include "../governor.h"
#define DEVFREQ_MIF_REBOOT_FREQ (900000)
#define DEVFREQ_MIF_SWITCH_FREQ (667000)
u32 sw_volt_table;
int is_dll_on(void)
{
return cal_dfs_ext_ctrl(dvfs_mif, cal_dfs_mif_is_dll_on, 0);
}
EXPORT_SYMBOL_GPL(is_dll_on);
static struct exynos_devfreq_data *mif_data;
static int exynos7870_devfreq_mif_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
mutex_lock(&data->devfreq->lock);
cal_vclk_dbg_info(dvfs_mif);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos7870_devfreq_mif_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_MIF_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos7870_devfreq_cl_dvfs_start(struct exynos_devfreq_data *data)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_CL_DVFS_MIF
ret = exynos_cl_dvfs_start(ID_MIF);
#endif
return ret;
}
static int exynos7870_devfreq_cl_dvfs_stop(u32 target_idx,
struct exynos_devfreq_data *data)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_CL_DVFS_MIF
ret = exynos_cl_dvfs_stop(ID_MIF, target_idx);
#endif
return ret;
}
static int exynos7870_devfreq_mif_get_switch_freq(u32 cur_freq, u32 new_freq,
u32 *switch_freq)
{
*switch_freq = DEVFREQ_MIF_SWITCH_FREQ;
return 0;
}
static int exynos7870_devfreq_mif_get_switch_voltage(u32 cur_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (DEVFREQ_MIF_SWITCH_FREQ >= cur_freq)
if (new_freq >= DEVFREQ_MIF_SWITCH_FREQ)
data->switch_volt = data->new_volt;
else
data->switch_volt = sw_volt_table;
else
if (cur_freq >= new_freq)
data->switch_volt = data->old_volt;
else
data->switch_volt = data->new_volt;
//pr_info("Selected switching voltage: %uuV\n", data->switch_volt);
return 0;
}
static int exynos7870_devfreq_mif_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)clk_get_rate(data->clk);
if (*cur_freq == 0) {
dev_err(dev, "failed get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_mif_change_to_switch_freq(struct device *dev,
struct exynos_devfreq_data *data)
{
if (clk_set_rate(data->sw_clk, data->switch_freq)) {
dev_err(dev, "failed to set switching frequency by CAL (%uKhz for %uKhz)\n",
data->switch_freq, data->new_freq);
return -EINVAL;
}
return 0;
}
static int exynos7870_devfreq_mif_restore_from_switch_freq(struct device *dev,
struct exynos_devfreq_data *data)
{
if (clk_set_rate(data->clk, data->new_freq)) {
dev_err(dev, "failed to set frequency by CAL (%uKhz)\n",
data->new_freq);
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_mif_set_freq_post(struct device *dev,
struct exynos_devfreq_data *data)
{
/* Send information about MIF frequency to mailbox */
mbox_set_value(MCU_CP, MCU_IPC_INT13, data->new_freq);
return 0;
}
static int exynos7870_devfreq_mif_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq, cur_freq;
unsigned long tmp_max, tmp_min;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i, ret;
ret = cal_clk_enable(dvfs_mif);
if (ret) {
dev_err(dev, "failed to enable MIF\n");
return -EINVAL;
}
max_freq = (u32)cal_dfs_get_max_freq(dvfs_mif);
if (!max_freq) {
dev_err(dev, "failed get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_max = (unsigned long)max_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_max, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_mif);
if (!min_freq) {
dev_err(dev, "failed get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
tmp_min = (unsigned long)min_freq;
target_opp = devfreq_recommended_opp(dev, &tmp_min, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
cur_freq = clk_get_rate(data->clk);
dev_info(dev, "current frequency: %uKhz\n", cur_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos7870_devfreq_mif_get_volt_table(struct device *dev, u32 *volt_table,
struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt mif_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_mif, mif_rate_volt);
if (!table_size) {
dev_err(dev, "failed get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(mif_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)mif_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)mif_rate_volt[i].volt;
/* Fill switch voltage table */
if (!sw_volt_table &&
data->opp_list[i].freq < DEVFREQ_MIF_SWITCH_FREQ)
sw_volt_table = (u32)mif_rate_volt[i-1].volt;
}
dev_info(dev, "SW_volt %uuV in freq %uKhz\n",
sw_volt_table, DEVFREQ_MIF_SWITCH_FREQ);
return 0;
}
static int exynos7870_mif_ppmu_register(struct device *dev,
struct exynos_devfreq_data *data)
{
#if 0
int ret;
struct devfreq_exynos *ppmu_data = (struct devfreq_exynos *)&data->ppmu_data;
ret = exynos7870_devfreq_register(ppmu_data);
if (ret) {
dev_err(dev, "failed ppmu register\n");
return ret;
}
ret = exynos7870_ppmu_register_notifier(MIF, &data->ppmu_nb->nb);
if (ret) {
dev_err(dev, "failed ppmu notifier register\n");
return ret;
}
#endif
return 0;
}
static int exynos7870_mif_ppmu_unregister(struct device *dev,
struct exynos_devfreq_data *data)
{
#if 0
exynos7870_ppmu_unregister_notifier(MIF, &data->ppmu_nb->nb);
#endif
return 0;
}
static int exynos7870_devfreq_mif_init(struct device *dev,
struct exynos_devfreq_data *data)
{
data->clk = clk_get(dev, "dvfs_mif");
if (IS_ERR_OR_NULL(data->clk)) {
dev_err(dev, "failed get dvfs vclk\n");
return -ENODEV;
}
data->sw_clk = clk_get(dev, "dvfs_mif_sw");
if (IS_ERR_OR_NULL(data->sw_clk)) {
dev_err(dev, "failed get dvfs sw vclk\n");
clk_put(data->clk);
return -ENODEV;
}
return 0;
}
static int exynos7870_devfreq_mif_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
clk_put(data->sw_clk);
clk_put(data->clk);
return 0;
}
static int __init exynos7870_devfreq_mif_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos7870_devfreq_mif_init;
data->ops.exit = exynos7870_devfreq_mif_exit;
data->ops.get_volt_table = exynos7870_devfreq_mif_get_volt_table;
data->ops.ppmu_register = exynos7870_mif_ppmu_register;
data->ops.ppmu_unregister = exynos7870_mif_ppmu_unregister;
data->ops.get_switch_freq = exynos7870_devfreq_mif_get_switch_freq;
data->ops.get_switch_voltage = exynos7870_devfreq_mif_get_switch_voltage;
data->ops.get_freq = exynos7870_devfreq_mif_get_freq;
data->ops.change_to_switch_freq = exynos7870_devfreq_mif_change_to_switch_freq;
data->ops.restore_from_switch_freq = exynos7870_devfreq_mif_restore_from_switch_freq;
data->ops.set_freq_post = exynos8890_devfreq_mif_set_freq_post;
data->ops.init_freq_table = exynos7870_devfreq_mif_init_freq_table;
data->ops.cl_dvfs_start = exynos7870_devfreq_cl_dvfs_start;
data->ops.cl_dvfs_stop = exynos7870_devfreq_cl_dvfs_stop;
data->ops.reboot = exynos7870_devfreq_mif_reboot;
data->ops.cmu_dump = exynos7870_devfreq_mif_cmu_dump;
mif_data = data;
return 0;
}
static int __init exynos7870_devfreq_mif_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_MIF,
exynos7870_devfreq_mif_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos7870_devfreq_mif_initcall);

View file

@ -0,0 +1,221 @@
/* linux/drivers/devfreq/exynos8890_bus_cam.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS8890 SoC CAM devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E8890/S5E8890-vclk.h"
#include "../governor.h"
#define DEVFREQ_CAM_REBOOT_FREQ (600000)
static struct exynos_devfreq_data *cam_data;
static int exynos8890_devfreq_cam_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_cam);
return 0;
}
static int exynos8890_devfreq_cam_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_CAM_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static u32 exynos8890_devfreq_cam_get_target_freq(char *name, u32 freq)
{
return cal_dfs_get_rate_by_member(dvfs_cam, name, freq);
}
static int exynos8890_devfreq_cam_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)cal_dfs_get_rate(dvfs_cam);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_cam_set_freq(struct device *dev,
u32 old_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (cal_dfs_set_rate(dvfs_cam, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n",
new_freq);
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_cam_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_cam);
if (!max_freq) {
dev_err(dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev,
(unsigned long *)&max_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_cam);
if (!min_freq) {
dev_err(dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev,
(unsigned long *)&min_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos8890_devfreq_cam_get_volt_table(struct device *dev,
u32 *volt_table, struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt cam_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_cam, cam_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(cam_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)cam_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)cam_rate_volt[i].volt;
}
return 0;
}
static int exynos8890_devfreq_cam_init(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int exynos8890_devfreq_cam_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int __init exynos8890_devfreq_cam_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos8890_devfreq_cam_init;
data->ops.exit = exynos8890_devfreq_cam_exit;
data->ops.get_volt_table = exynos8890_devfreq_cam_get_volt_table;
data->ops.get_freq = exynos8890_devfreq_cam_get_freq;
data->ops.set_freq = exynos8890_devfreq_cam_set_freq;
data->ops.init_freq_table = exynos8890_devfreq_cam_init_freq_table;
data->ops.get_target_freq = exynos8890_devfreq_cam_get_target_freq;
data->ops.reboot = exynos8890_devfreq_cam_reboot;
data->ops.cmu_dump = exynos8890_devfreq_cam_cmu_dump;
cam_data = data;
return 0;
}
static int __init exynos8890_devfreq_cam_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_CAM,
exynos8890_devfreq_cam_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos8890_devfreq_cam_initcall);

View file

@ -0,0 +1,221 @@
/* linux/drivers/devfreq/exynos8890_bus_disp.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS8890 SoC DISP devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E8890/S5E8890-vclk.h"
#include "../governor.h"
#define DEVFREQ_DISP_REBOOT_FREQ (400000)
static struct exynos_devfreq_data *disp_data;
static int exynos8890_devfreq_disp_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_disp);
return 0;
}
static int exynos8890_devfreq_disp_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_DISP_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static u32 exynos8890_devfreq_disp_get_target_freq(char *name, u32 freq)
{
return cal_dfs_get_rate_by_member(dvfs_disp, name, freq);
}
static int exynos8890_devfreq_disp_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)cal_dfs_get_rate(dvfs_disp);
if (*cur_freq == 0) {
dev_err(dev, "failed to get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_disp_set_freq(struct device *dev,
u32 old_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (cal_dfs_set_rate(dvfs_disp, (unsigned long)new_freq)) {
dev_err(dev, "failed to set frequency via CAL (%uKhz)\n",
new_freq);
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_disp_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_disp);
if (!max_freq) {
dev_err(dev, "failed to get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev,
(unsigned long *)&max_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_disp);
if (!min_freq) {
dev_err(dev, "failed to get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev,
(unsigned long *)&min_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos8890_devfreq_disp_get_volt_table(struct device *dev,
u32 *volt_table, struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt disp_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_disp, disp_rate_volt);
if (!table_size) {
dev_err(dev, "failed to get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(disp_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)disp_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)disp_rate_volt[i].volt;
}
return 0;
}
static int exynos8890_devfreq_disp_init(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int exynos8890_devfreq_disp_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int __init exynos8890_devfreq_disp_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos8890_devfreq_disp_init;
data->ops.exit = exynos8890_devfreq_disp_exit;
data->ops.get_volt_table = exynos8890_devfreq_disp_get_volt_table;
data->ops.get_freq = exynos8890_devfreq_disp_get_freq;
data->ops.set_freq = exynos8890_devfreq_disp_set_freq;
data->ops.init_freq_table = exynos8890_devfreq_disp_init_freq_table;
data->ops.get_target_freq = exynos8890_devfreq_disp_get_target_freq;
data->ops.reboot = exynos8890_devfreq_disp_reboot;
data->ops.cmu_dump = exynos8890_devfreq_disp_cmu_dump;
disp_data = data;
return 0;
}
static int __init exynos8890_devfreq_disp_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_DISP,
exynos8890_devfreq_disp_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos8890_devfreq_disp_initcall);

View file

@ -0,0 +1,220 @@
/* linux/drivers/devfreq/exynos8890_bus_int.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS8890 SoC INT devfreq driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <soc/samsung/exynos-devfreq.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E8890/S5E8890-vclk.h"
#include "../governor.h"
#define DEVFREQ_INT_REBOOT_FREQ (690000)
static struct exynos_devfreq_data *int_data;
static int exynos8890_devfreq_int_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
cal_vclk_dbg_info(dvfs_int);
return 0;
}
static int exynos8890_devfreq_int_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_INT_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static u32 exynos8890_devfreq_int_get_target_freq(char *name, u32 freq)
{
return cal_dfs_get_rate_by_member(dvfs_int, name, freq);
}
static int exynos8890_devfreq_int_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)cal_dfs_get_rate(dvfs_int);
if (*cur_freq == 0) {
dev_err(dev, "failed get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_int_set_freq(struct device *dev,
u32 old_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
if (cal_dfs_set_rate(dvfs_int, (unsigned long)new_freq)) {
dev_err(dev, "failed set frequency to CAL (%uKhz)\n",
new_freq);
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_int_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i;
max_freq = (u32)cal_dfs_get_max_freq(dvfs_int);
if (!max_freq) {
dev_err(dev, "failed get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev, (unsigned long *)&max_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_int);
if (!min_freq) {
dev_err(dev, "failed get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev, (unsigned long *)&min_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos8890_devfreq_int_get_volt_table(struct device *dev, u32 *volt_table,
struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt int_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_int, int_rate_volt);
if (!table_size) {
dev_err(dev, "failed get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(int_rate_volt[i].rate)) {
dev_err(dev, "Freq tablle is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)int_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)int_rate_volt[i].volt;
}
return 0;
}
static int exynos8890_devfreq_int_init(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int exynos8890_devfreq_int_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int __init exynos8890_devfreq_int_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos8890_devfreq_int_init;
data->ops.exit = exynos8890_devfreq_int_exit;
data->ops.get_volt_table = exynos8890_devfreq_int_get_volt_table;
data->ops.get_freq = exynos8890_devfreq_int_get_freq;
data->ops.set_freq = exynos8890_devfreq_int_set_freq;
data->ops.init_freq_table = exynos8890_devfreq_int_init_freq_table;
data->ops.get_target_freq = exynos8890_devfreq_int_get_target_freq;
data->ops.reboot = exynos8890_devfreq_int_reboot;
data->ops.cmu_dump = exynos8890_devfreq_int_cmu_dump;
int_data = data;
return 0;
}
static int __init exynos8890_devfreq_int_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_INT,
exynos8890_devfreq_int_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos8890_devfreq_int_initcall);

View file

@ -0,0 +1,373 @@
/* linux/drivers/devfreq/exynos8890_bus_mif.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS8890 SoC MIF devfreq driver
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <soc/samsung/exynos-devfreq.h>
#include <soc/samsung/bts.h>
#include <linux/apm-exynos.h>
#include <soc/samsung/asv-exynos.h>
#include "../../../drivers/soc/samsung/pwrcal/pwrcal.h"
#include "../../../drivers/soc/samsung/pwrcal/S5E8890/S5E8890-vclk.h"
#include "../governor.h"
#if 0
#include "exynos8890_ppmu.h"
#endif
#define DEVFREQ_MIF_REBOOT_FREQ (3078000/2)
#define DEVFREQ_MIF_DIFF_FREQ (962000)
#define DEVFREQ_MIF_CMOS_FREQ (468000)
#define DEVFREQ_MIF_SWITCH_FREQ_HI (936000)
#define DEVFREQ_MIF_SWITCH_FREQ (468000)
#define SWITCH_CMOS_VOLT_OFFSET (56250)
u32 sw_volt_table[2];
int is_dll_on(void)
{
return cal_dfs_ext_ctrl(dvfs_mif, cal_dfs_mif_is_dll_on, 0);
}
EXPORT_SYMBOL_GPL(is_dll_on);
static struct exynos_devfreq_data *mif_data;
static int exynos8890_devfreq_mif_cmu_dump(struct device *dev,
struct exynos_devfreq_data *data)
{
mutex_lock(&data->devfreq->lock);
cal_vclk_dbg_info(dvfs_mif);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos8890_devfreq_mif_reboot(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 freq = DEVFREQ_MIF_REBOOT_FREQ;
data->max_freq = freq;
data->devfreq->max_freq = data->max_freq;
mutex_lock(&data->devfreq->lock);
update_devfreq(data->devfreq);
mutex_unlock(&data->devfreq->lock);
return 0;
}
static int exynos8890_devfreq_cl_dvfs_start(struct exynos_devfreq_data *data)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_CL_DVFS_MIF
ret = exynos_cl_dvfs_start(ID_MIF);
#endif
return ret;
}
static int exynos8890_devfreq_cl_dvfs_stop(u32 target_idx,
struct exynos_devfreq_data *data)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_CL_DVFS_MIF
ret = exynos_cl_dvfs_stop(ID_MIF, target_idx);
#endif
return ret;
}
static int exynos8890_devfreq_mif_get_switch_freq(u32 cur_freq, u32 new_freq,
u32 *switch_freq)
{
*switch_freq = DEVFREQ_MIF_SWITCH_FREQ;
if (cur_freq > DEVFREQ_MIF_DIFF_FREQ ||
new_freq > DEVFREQ_MIF_DIFF_FREQ)
*switch_freq = DEVFREQ_MIF_SWITCH_FREQ_HI;
if (cur_freq <= DEVFREQ_MIF_CMOS_FREQ ||
new_freq <= DEVFREQ_MIF_CMOS_FREQ)
*switch_freq = DEVFREQ_MIF_SWITCH_FREQ;
return 0;
}
static int exynos8890_devfreq_mif_get_switch_voltage(u32 cur_freq, u32 new_freq,
struct exynos_devfreq_data *data)
{
/* if switching PLL is CMOS I/O mode level, it should be add a voltage offset */
if (cur_freq <= DEVFREQ_MIF_CMOS_FREQ || new_freq <= DEVFREQ_MIF_CMOS_FREQ) {
data->switch_volt = sw_volt_table[1] + SWITCH_CMOS_VOLT_OFFSET;
goto out;
}
if (cur_freq > DEVFREQ_MIF_DIFF_FREQ && new_freq > DEVFREQ_MIF_DIFF_FREQ) {
data->switch_volt = 0;
goto out;
}
if (cur_freq >= new_freq)
data->switch_volt = data->old_volt;
else if (cur_freq < new_freq)
data->switch_volt = data->new_volt;
out:
//pr_info("Selected switching voltage: %uuV\n", data->switch_volt);
return 0;
}
static int exynos8890_devfreq_mif_get_freq(struct device *dev, u32 *cur_freq,
struct exynos_devfreq_data *data)
{
*cur_freq = (u32)cal_dfs_get_rate(dvfs_mif);
if (*cur_freq == 0) {
dev_err(dev, "failed get frequency from CAL\n");
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_mif_change_to_switch_freq(struct device *dev,
struct exynos_devfreq_data *data)
{
if (cal_dfs_set_rate_switch(dvfs_mif, data->switch_freq)) {
dev_err(dev, "failed to set switching frequency by CAL (%uKhz for %uKhz)\n",
data->switch_freq, data->new_freq);
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_mif_restore_from_switch_freq(struct device *dev,
struct exynos_devfreq_data *data)
{
if (cal_dfs_set_rate(dvfs_mif, data->new_freq)) {
dev_err(dev, "failed to set frequency by CAL (%uKhz)\n",
data->new_freq);
return -EINVAL;
}
return 0;
}
static int exynos8890_devfreq_mif_init_freq_table(struct device *dev,
struct exynos_devfreq_data *data)
{
u32 max_freq, min_freq, cur_freq;
struct dev_pm_opp *target_opp;
u32 flags = 0;
int i, ret;
ret = cal_clk_enable(dvfs_mif);
if (ret) {
dev_err(dev, "failed to enable MIF\n");
return -EINVAL;
}
max_freq = (u32)cal_dfs_get_max_freq(dvfs_mif);
if (!max_freq) {
dev_err(dev, "failed get max frequency\n");
return -EINVAL;
}
dev_info(dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
data->max_freq, max_freq);
if (max_freq < data->max_freq) {
rcu_read_lock();
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev, (unsigned long *)&max_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for max_freq\n");
return PTR_ERR(target_opp);
}
data->max_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
min_freq = (u32)cal_dfs_get_min_freq(dvfs_mif);
if (!min_freq) {
dev_err(dev, "failed get min frequency\n");
return -EINVAL;
}
dev_info(dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
data->min_freq, min_freq);
if (min_freq > data->min_freq) {
rcu_read_lock();
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
target_opp = devfreq_recommended_opp(dev, (unsigned long *)&min_freq, flags);
if (IS_ERR(target_opp)) {
rcu_read_unlock();
dev_err(dev, "not found valid OPP for min_freq\n");
return PTR_ERR(target_opp);
}
data->min_freq = dev_pm_opp_get_freq(target_opp);
rcu_read_unlock();
}
dev_info(dev, "min_freq: %uKhz, max_freq: %uKhz\n",
data->min_freq, data->max_freq);
cur_freq = cal_dfs_get_rate(dvfs_mif);
dev_info(dev, "current frequency: %uKhz\n", cur_freq);
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq > data->max_freq ||
data->opp_list[i].freq < data->min_freq)
dev_pm_opp_disable(dev, (unsigned long)data->opp_list[i].freq);
}
return 0;
}
static int exynos8890_devfreq_mif_get_volt_table(struct device *dev, u32 *volt_table,
struct exynos_devfreq_data *data)
{
struct dvfs_rate_volt mif_rate_volt[data->max_state];
int table_size;
int i;
table_size = cal_dfs_get_rate_asv_table(dvfs_mif, mif_rate_volt);
if (!table_size) {
dev_err(dev, "failed get ASV table\n");
return -ENODEV;
}
if (table_size != data->max_state) {
dev_err(dev, "ASV table size is not matched\n");
return -ENODEV;
}
for (i = 0; i < data->max_state; i++) {
if (data->opp_list[i].freq != (u32)(mif_rate_volt[i].rate)) {
dev_err(dev, "Freq table is not matched(%u:%u)\n",
data->opp_list[i].freq, (u32)mif_rate_volt[i].rate);
return -EINVAL;
}
volt_table[i] = (u32)mif_rate_volt[i].volt;
/* Fill switch voltage table */
if (!sw_volt_table[0] &&
data->opp_list[i].freq < DEVFREQ_MIF_SWITCH_FREQ_HI)
sw_volt_table[0] = (u32)mif_rate_volt[i-1].volt;
if (!sw_volt_table[1] &&
data->opp_list[i].freq < DEVFREQ_MIF_SWITCH_FREQ)
sw_volt_table[1] = (u32)mif_rate_volt[i-1].volt;
}
dev_info(dev, "SW_volt %uuV in freq %uKhz\n",
sw_volt_table[0], DEVFREQ_MIF_SWITCH_FREQ_HI);
dev_info(dev, "SW_volt %uuV in freq %uKhz\n",
sw_volt_table[1], DEVFREQ_MIF_SWITCH_FREQ);
return 0;
}
static int exynos8890_mif_ppmu_register(struct device *dev,
struct exynos_devfreq_data *data)
{
#if 0
int ret;
struct devfreq_exynos *ppmu_data = (struct devfreq_exynos *)&data->ppmu_data;
ret = exynos8890_devfreq_register(ppmu_data);
if (ret) {
dev_err(dev, "failed ppmu register\n");
return ret;
}
ret = exynos8890_ppmu_register_notifier(MIF, &data->ppmu_nb->nb);
if (ret) {
dev_err(dev, "failed ppmu notifier register\n");
return ret;
}
#endif
return 0;
}
static int exynos8890_mif_ppmu_unregister(struct device *dev,
struct exynos_devfreq_data *data)
{
#if 0
exynos8890_ppmu_unregister_notifier(MIF, &data->ppmu_nb->nb);
#endif
return 0;
}
static int exynos8890_devfreq_mif_init(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int exynos8890_devfreq_mif_exit(struct device *dev,
struct exynos_devfreq_data *data)
{
return 0;
}
static int __init exynos8890_devfreq_mif_init_prepare(struct exynos_devfreq_data *data)
{
data->ops.init = exynos8890_devfreq_mif_init;
data->ops.exit = exynos8890_devfreq_mif_exit;
data->ops.get_volt_table = exynos8890_devfreq_mif_get_volt_table;
data->ops.ppmu_register = exynos8890_mif_ppmu_register;
data->ops.ppmu_unregister = exynos8890_mif_ppmu_unregister;
data->ops.get_switch_freq = exynos8890_devfreq_mif_get_switch_freq;
data->ops.get_switch_voltage = exynos8890_devfreq_mif_get_switch_voltage;
data->ops.get_freq = exynos8890_devfreq_mif_get_freq;
data->ops.change_to_switch_freq = exynos8890_devfreq_mif_change_to_switch_freq;
data->ops.restore_from_switch_freq = exynos8890_devfreq_mif_restore_from_switch_freq;
data->ops.init_freq_table = exynos8890_devfreq_mif_init_freq_table;
data->ops.cl_dvfs_start = exynos8890_devfreq_cl_dvfs_start;
data->ops.cl_dvfs_stop = exynos8890_devfreq_cl_dvfs_stop;
data->ops.reboot = exynos8890_devfreq_mif_reboot;
data->ops.cmu_dump = exynos8890_devfreq_mif_cmu_dump;
mif_data = data;
return 0;
}
static int __init exynos8890_devfreq_mif_initcall(void)
{
if (register_exynos_devfreq_init_prepare(DEVFREQ_MIF,
exynos8890_devfreq_mif_init_prepare))
return -EINVAL;
return 0;
}
fs_initcall(exynos8890_devfreq_mif_initcall);

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS - PPMU support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/io.h>
#include "exynos_ppmu.h"
void exynos_ppmu_reset(void __iomem *ppmu_base)
{
__raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, ppmu_base);
__raw_writel(PPMU_ENABLE_CYCLE |
PPMU_ENABLE_COUNT0 |
PPMU_ENABLE_COUNT1 |
PPMU_ENABLE_COUNT2 |
PPMU_ENABLE_COUNT3,
ppmu_base + PPMU_CNTENS);
}
void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,
unsigned int evt)
{
__raw_writel(evt, ppmu_base + PPMU_BEVTSEL(ch));
}
void exynos_ppmu_start(void __iomem *ppmu_base)
{
__raw_writel(PPMU_ENABLE, ppmu_base);
}
void exynos_ppmu_stop(void __iomem *ppmu_base)
{
__raw_writel(PPMU_DISABLE, ppmu_base);
}
unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch)
{
unsigned int total;
if (ch == PPMU_PMNCNT3)
total = ((__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) |
__raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1)));
else
total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch));
return total;
}
void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data)
{
unsigned int i;
for (i = 0; i < ppmu_data->ppmu_end; i++) {
void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base;
/* Reset the performance and cycle counters */
exynos_ppmu_reset(ppmu_base);
/* Setup count registers to monitor read/write transactions */
ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
ppmu_data->ppmu[i].event[PPMU_PMNCNT3]);
exynos_ppmu_start(ppmu_base);
}
}
EXPORT_SYMBOL(busfreq_mon_reset);
void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
{
int i, j;
for (i = 0; i < ppmu_data->ppmu_end; i++) {
void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base;
exynos_ppmu_stop(ppmu_base);
/* Update local data from PPMU */
ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
if (ppmu_data->ppmu[i].event[j] == 0)
ppmu_data->ppmu[i].count[j] = 0;
else
ppmu_data->ppmu[i].count[j] =
exynos_ppmu_read(ppmu_base, j);
}
}
busfreq_mon_reset(ppmu_data);
}
EXPORT_SYMBOL(exynos_read_ppmu);
int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
{
unsigned int count = 0;
int i, j, busy = 0;
for (i = 0; i < ppmu_data->ppmu_end; i++) {
for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
if (ppmu_data->ppmu[i].count[j] > count) {
count = ppmu_data->ppmu[i].count[j];
busy = i;
}
}
}
return busy;
}
EXPORT_SYMBOL(exynos_get_busier_ppmu);

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS PPMU header
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DEVFREQ_EXYNOS_PPMU_H
#define __DEVFREQ_EXYNOS_PPMU_H __FILE__
#include <linux/ktime.h>
/* For PPMU Control */
#define PPMU_ENABLE BIT(0)
#define PPMU_DISABLE 0x0
#define PPMU_CYCLE_RESET BIT(1)
#define PPMU_COUNTER_RESET BIT(2)
#define PPMU_ENABLE_COUNT0 BIT(0)
#define PPMU_ENABLE_COUNT1 BIT(1)
#define PPMU_ENABLE_COUNT2 BIT(2)
#define PPMU_ENABLE_COUNT3 BIT(3)
#define PPMU_ENABLE_CYCLE BIT(31)
#define PPMU_CNTENS 0x10
#define PPMU_FLAG 0x50
#define PPMU_CCNT_OVERFLOW BIT(31)
#define PPMU_CCNT 0x100
#define PPMU_PMCNT0 0x110
#define PPMU_PMCNT_OFFSET 0x10
#define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x))
#define PPMU_BEVT0SEL 0x1000
#define PPMU_BEVTSEL_OFFSET 0x100
#define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET))
/* For Event Selection */
#define RD_DATA_COUNT 0x5
#define WR_DATA_COUNT 0x6
#define RDWR_DATA_COUNT 0x7
enum ppmu_counter {
PPMU_PMNCNT0,
PPMU_PMCCNT1,
PPMU_PMNCNT2,
PPMU_PMNCNT3,
PPMU_PMNCNT_MAX,
};
struct bus_opp_table {
unsigned int idx;
unsigned long clk;
unsigned long volt;
};
struct exynos_ppmu {
void __iomem *hw_base;
unsigned int ccnt;
unsigned int event[PPMU_PMNCNT_MAX];
unsigned int count[PPMU_PMNCNT_MAX];
unsigned long long ns;
ktime_t reset_time;
bool ccnt_overflow;
bool count_overflow[PPMU_PMNCNT_MAX];
};
struct busfreq_ppmu_data {
struct exynos_ppmu *ppmu;
int ppmu_end;
};
void exynos_ppmu_reset(void __iomem *ppmu_base);
void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,
unsigned int evt);
void exynos_ppmu_start(void __iomem *ppmu_base);
void exynos_ppmu_stop(void __iomem *ppmu_base);
unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch);
void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data);
void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data);
int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data);
#endif /* __DEVFREQ_EXYNOS_PPMU_H */