mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 16:58:04 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
147
drivers/devfreq/Kconfig
Normal file
147
drivers/devfreq/Kconfig
Normal file
|
@ -0,0 +1,147 @@
|
|||
menuconfig PM_DEVFREQ
|
||||
bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support"
|
||||
help
|
||||
A device may have a list of frequencies and voltages available.
|
||||
devfreq, a generic DVFS framework can be registered for a device
|
||||
in order to let the governor provided to devfreq choose an
|
||||
operating frequency based on the device driver's policy.
|
||||
|
||||
Each device may have its own governor and policy. Devfreq can
|
||||
reevaluate the device state periodically and/or based on the
|
||||
notification to "nb", a notifier block, of devfreq.
|
||||
|
||||
Like some CPUs with CPUfreq, a device may have multiple clocks.
|
||||
However, because the clock frequencies of a single device are
|
||||
determined by the single device's state, an instance of devfreq
|
||||
is attached to a single device and returns a "representative"
|
||||
clock frequency of the device, which is also attached
|
||||
to a device by 1-to-1. The device registering devfreq takes the
|
||||
responsibility to "interpret" the representative frequency and
|
||||
to set its every clock accordingly with the "target" callback
|
||||
given to devfreq.
|
||||
|
||||
When OPP is used with the devfreq device, it is recommended to
|
||||
register devfreq's nb to the OPP's notifier head. If OPP is
|
||||
used with the devfreq device, you may use OPP helper
|
||||
functions defined in devfreq.h.
|
||||
|
||||
if PM_DEVFREQ
|
||||
|
||||
comment "DEVFREQ Governors"
|
||||
|
||||
config DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
tristate "Simple Ondemand"
|
||||
help
|
||||
Chooses frequency based on the recent load on the device. Works
|
||||
similar as ONDEMAND governor of CPUFREQ does. A device with
|
||||
Simple-Ondemand should be able to provide busy/total counter
|
||||
values that imply the usage rate. A device may provide tuned
|
||||
values to the governor with data field at devfreq_add_device().
|
||||
|
||||
config DEVFREQ_GOV_SIMPLE_USAGE
|
||||
tristate "Simple Usage"
|
||||
help
|
||||
Chooses frequency based on the percentage of target device.
|
||||
|
||||
config DEVFREQ_GOV_SIMPLE_EXYNOS
|
||||
tristate "Simple Exynos"
|
||||
help
|
||||
Chooses frequency based on the threshold of target device.
|
||||
|
||||
config DEVFREQ_GOV_SIMPLE_INTERACTIVE
|
||||
tristate "Simple Interactive"
|
||||
help
|
||||
Chooses frequency based on the requested PM QoS from target device.
|
||||
And This governor uses timer when change frequency.
|
||||
|
||||
config DEVFREQ_GOV_PERFORMANCE
|
||||
tristate "Performance"
|
||||
help
|
||||
Sets the frequency at the maximum available frequency.
|
||||
This governor always returns UINT_MAX as frequency so that
|
||||
the DEVFREQ framework returns the highest frequency available
|
||||
at any time.
|
||||
|
||||
config DEVFREQ_GOV_POWERSAVE
|
||||
tristate "Powersave"
|
||||
help
|
||||
Sets the frequency at the minimum available frequency.
|
||||
This governor always returns 0 as frequency so that
|
||||
the DEVFREQ framework returns the lowest frequency available
|
||||
at any time.
|
||||
|
||||
config DEVFREQ_GOV_USERSPACE
|
||||
tristate "Userspace"
|
||||
help
|
||||
Sets the frequency at the user specified one.
|
||||
This governor returns the user configured frequency if there
|
||||
has been an input to /sys/devices/.../power/devfreq_set_freq.
|
||||
Otherwise, the governor does not change the frequnecy
|
||||
given at the initialization.
|
||||
|
||||
comment "DEVFREQ Drivers"
|
||||
|
||||
config ARM_EXYNOS_DEVFREQ
|
||||
bool "ARM Exynos Memory Bus DEVFREQ Common driver"
|
||||
depends on ARCH_EXYNOS
|
||||
select ARCH_HAS_OPP
|
||||
select PM_OPP
|
||||
help
|
||||
This adds the DEVFREQ driver for Exynos series common bus.
|
||||
|
||||
config ARM_EXYNOS_DEVFREQ_DEBUG
|
||||
bool "ARM Exynos DEVFREQ sysfs for Debug"
|
||||
depends on ARM_EXYNOS_DEVFREQ
|
||||
|
||||
config ARM_EXYNOS4_BUS_DEVFREQ
|
||||
bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver"
|
||||
depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_OPP
|
||||
help
|
||||
This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int)
|
||||
and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int).
|
||||
It reads PPMU counters of memory controllers and adjusts
|
||||
the operating frequencies and voltages with OPP support.
|
||||
This does not yet operate with optimal voltages.
|
||||
|
||||
config ARM_EXYNOS5_BUS_DEVFREQ
|
||||
tristate "ARM Exynos5250 Bus DEVFREQ Driver"
|
||||
depends on SOC_EXYNOS5250
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_OPP
|
||||
help
|
||||
This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).
|
||||
It reads PPMU counters of memory controllers and adjusts the
|
||||
operating frequencies and voltages with OPP support.
|
||||
|
||||
config ARM_EXYNOS8890_BUS_DEVFREQ
|
||||
bool "ARM Exynos8890 Memory Bus DEVFREQ Driver"
|
||||
default y
|
||||
depends on ARM_EXYNOS_DEVFREQ
|
||||
depends on SOC_EXYNOS8890
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select DEVFREQ_GOV_SIMPLE_EXYNOS
|
||||
help
|
||||
This adds the DEVFREQ driver for Exynos8890 memory and int.
|
||||
|
||||
config ARM_EXYNOS7870_BUS_DEVFREQ
|
||||
bool "ARM Exynos7870 Memory Bus DEVFREQ Driver"
|
||||
default y
|
||||
depends on ARM_EXYNOS_DEVFREQ
|
||||
depends on SOC_EXYNOS7870
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select DEVFREQ_GOV_SIMPLE_EXYNOS
|
||||
help
|
||||
This adds the DEVFREQ driver for Exynos7870 memory and int.
|
||||
|
||||
config ARM_EXYNOS7570_BUS_DEVFREQ
|
||||
bool "ARM Exynos7570 Memory Bus DEVFREQ Driver"
|
||||
default y
|
||||
depends on ARM_EXYNOS_DEVFREQ
|
||||
depends on SOC_EXYNOS7570
|
||||
select DEVFREQ_GOV_SIMPLE_INTERACTIVE
|
||||
help
|
||||
This adds the DEVFREQ driver for Exynos7570 memory and int.
|
||||
|
||||
endif # PM_DEVFREQ
|
13
drivers/devfreq/Makefile
Normal file
13
drivers/devfreq/Makefile
Normal file
|
@ -0,0 +1,13 @@
|
|||
obj-$(CONFIG_PM_DEVFREQ) += devfreq.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_EXYNOS) += governor_simpleexynos.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE) += governor_simpleinteractive.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_USAGE) += governor_simpleusage.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
|
||||
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
|
||||
|
||||
# DEVFREQ Drivers
|
||||
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/
|
||||
obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/
|
||||
obj-$(CONFIG_ARM_EXYNOS_DEVFREQ) += exynos/
|
1205
drivers/devfreq/devfreq.c
Normal file
1205
drivers/devfreq/devfreq.c
Normal file
File diff suppressed because it is too large
Load diff
7
drivers/devfreq/exynos/Makefile
Normal file
7
drivers/devfreq/exynos/Makefile
Normal 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
|
1979
drivers/devfreq/exynos/exynos-devfreq.c
Normal file
1979
drivers/devfreq/exynos/exynos-devfreq.c
Normal file
File diff suppressed because it is too large
Load diff
1056
drivers/devfreq/exynos/exynos4_bus.c
Normal file
1056
drivers/devfreq/exynos/exynos4_bus.c
Normal file
File diff suppressed because it is too large
Load diff
110
drivers/devfreq/exynos/exynos4_bus.h
Normal file
110
drivers/devfreq/exynos/exynos4_bus.h
Normal 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 */
|
432
drivers/devfreq/exynos/exynos5_bus.c
Normal file
432
drivers/devfreq/exynos/exynos5_bus.c
Normal 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");
|
209
drivers/devfreq/exynos/exynos7570_bus_cam.c
Normal file
209
drivers/devfreq/exynos/exynos7570_bus_cam.c
Normal 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);
|
209
drivers/devfreq/exynos/exynos7570_bus_disp.c
Normal file
209
drivers/devfreq/exynos/exynos7570_bus_disp.c
Normal 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);
|
209
drivers/devfreq/exynos/exynos7570_bus_int.c
Normal file
209
drivers/devfreq/exynos/exynos7570_bus_int.c
Normal 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);
|
319
drivers/devfreq/exynos/exynos7570_bus_mif.c
Normal file
319
drivers/devfreq/exynos/exynos7570_bus_mif.c
Normal 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);
|
231
drivers/devfreq/exynos/exynos7870_bus_cam.c
Normal file
231
drivers/devfreq/exynos/exynos7870_bus_cam.c
Normal 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);
|
231
drivers/devfreq/exynos/exynos7870_bus_disp.c
Normal file
231
drivers/devfreq/exynos/exynos7870_bus_disp.c
Normal 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);
|
232
drivers/devfreq/exynos/exynos7870_bus_int.c
Normal file
232
drivers/devfreq/exynos/exynos7870_bus_int.c
Normal 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);
|
377
drivers/devfreq/exynos/exynos7870_bus_mif.c
Normal file
377
drivers/devfreq/exynos/exynos7870_bus_mif.c
Normal 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);
|
221
drivers/devfreq/exynos/exynos8890_bus_cam.c
Normal file
221
drivers/devfreq/exynos/exynos8890_bus_cam.c
Normal 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);
|
221
drivers/devfreq/exynos/exynos8890_bus_disp.c
Normal file
221
drivers/devfreq/exynos/exynos8890_bus_disp.c
Normal 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);
|
220
drivers/devfreq/exynos/exynos8890_bus_int.c
Normal file
220
drivers/devfreq/exynos/exynos8890_bus_int.c
Normal 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);
|
373
drivers/devfreq/exynos/exynos8890_bus_mif.c
Normal file
373
drivers/devfreq/exynos/exynos8890_bus_mif.c
Normal 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);
|
119
drivers/devfreq/exynos/exynos_ppmu.c
Normal file
119
drivers/devfreq/exynos/exynos_ppmu.c
Normal 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);
|
86
drivers/devfreq/exynos/exynos_ppmu.h
Normal file
86
drivers/devfreq/exynos/exynos_ppmu.h
Normal 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 */
|
41
drivers/devfreq/governor.h
Normal file
41
drivers/devfreq/governor.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* governor.h - internal header for devfreq governors.
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This header is for devfreq governors in drivers/devfreq/
|
||||
*/
|
||||
|
||||
#ifndef _GOVERNOR_H
|
||||
#define _GOVERNOR_H
|
||||
|
||||
#include <linux/devfreq.h>
|
||||
|
||||
#define to_devfreq(DEV) container_of((DEV), struct devfreq, dev)
|
||||
|
||||
/* Devfreq events */
|
||||
#define DEVFREQ_GOV_START 0x1
|
||||
#define DEVFREQ_GOV_STOP 0x2
|
||||
#define DEVFREQ_GOV_INTERVAL 0x3
|
||||
#define DEVFREQ_GOV_SUSPEND 0x4
|
||||
#define DEVFREQ_GOV_RESUME 0x5
|
||||
|
||||
/* Caution: devfreq->lock must be locked before calling update_devfreq */
|
||||
extern int update_devfreq(struct devfreq *devfreq);
|
||||
|
||||
extern void devfreq_monitor_start(struct devfreq *devfreq);
|
||||
extern void devfreq_monitor_stop(struct devfreq *devfreq);
|
||||
extern void devfreq_monitor_suspend(struct devfreq *devfreq);
|
||||
extern void devfreq_monitor_resume(struct devfreq *devfreq);
|
||||
extern void devfreq_interval_update(struct devfreq *devfreq,
|
||||
unsigned int *delay);
|
||||
|
||||
extern int devfreq_add_governor(struct devfreq_governor *governor);
|
||||
extern int devfreq_remove_governor(struct devfreq_governor *governor);
|
||||
|
||||
#endif /* _GOVERNOR_H */
|
67
drivers/devfreq/governor_performance.c
Normal file
67
drivers/devfreq/governor_performance.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* linux/drivers/devfreq/governor_performance.c
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* 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/devfreq.h>
|
||||
#include <linux/module.h>
|
||||
#include "governor.h"
|
||||
|
||||
static int devfreq_performance_func(struct devfreq *df,
|
||||
unsigned long *freq)
|
||||
{
|
||||
/*
|
||||
* target callback should be able to get floor value as
|
||||
* said in devfreq.h
|
||||
*/
|
||||
if (!df->max_freq)
|
||||
*freq = UINT_MAX;
|
||||
else
|
||||
*freq = df->max_freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_performance_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (event == DEVFREQ_GOV_START) {
|
||||
mutex_lock(&devfreq->lock);
|
||||
ret = update_devfreq(devfreq);
|
||||
mutex_unlock(&devfreq->lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_performance = {
|
||||
.name = "performance",
|
||||
.get_target_freq = devfreq_performance_func,
|
||||
.event_handler = devfreq_performance_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_performance_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_performance);
|
||||
}
|
||||
subsys_initcall(devfreq_performance_init);
|
||||
|
||||
static void __exit devfreq_performance_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_performance);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_performance_exit);
|
||||
MODULE_LICENSE("GPL");
|
64
drivers/devfreq/governor_powersave.c
Normal file
64
drivers/devfreq/governor_powersave.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* linux/drivers/devfreq/governor_powersave.c
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* 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/devfreq.h>
|
||||
#include <linux/module.h>
|
||||
#include "governor.h"
|
||||
|
||||
static int devfreq_powersave_func(struct devfreq *df,
|
||||
unsigned long *freq)
|
||||
{
|
||||
/*
|
||||
* target callback should be able to get ceiling value as
|
||||
* said in devfreq.h
|
||||
*/
|
||||
*freq = df->min_freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_powersave_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (event == DEVFREQ_GOV_START) {
|
||||
mutex_lock(&devfreq->lock);
|
||||
ret = update_devfreq(devfreq);
|
||||
mutex_unlock(&devfreq->lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_powersave = {
|
||||
.name = "powersave",
|
||||
.get_target_freq = devfreq_powersave_func,
|
||||
.event_handler = devfreq_powersave_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_powersave_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_powersave);
|
||||
}
|
||||
subsys_initcall(devfreq_powersave_init);
|
||||
|
||||
static void __exit devfreq_powersave_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_powersave);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_powersave_exit);
|
||||
MODULE_LICENSE("GPL");
|
261
drivers/devfreq/governor_simpleexynos.c
Normal file
261
drivers/devfreq/governor_simpleexynos.c
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* linux/drivers/devfreq/governor_simpleexynos.c
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "governor.h"
|
||||
|
||||
static int gov_simple_exynos = 0;
|
||||
|
||||
static int devfreq_simple_exynos_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
struct devfreq_notifier_block *devfreq_nb;
|
||||
|
||||
devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
|
||||
|
||||
mutex_lock(&devfreq_nb->df->lock);
|
||||
update_devfreq(devfreq_nb->df);
|
||||
mutex_unlock(&devfreq_nb->df->lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/* Default constants for DevFreq-Simple-Exynos(DFE) */
|
||||
#define DFE_URGENTTHRESHOLD (65)
|
||||
#define DFE_UPTHRESHOLD (60)
|
||||
#define DFE_DOWNTHRESHOLD (45)
|
||||
#define DFE_IDLETHRESHOLD (30)
|
||||
|
||||
static int devfreq_simple_exynos_func(struct devfreq *df,
|
||||
unsigned long *freq)
|
||||
{
|
||||
struct devfreq_dev_status stat;
|
||||
unsigned int dfe_urgentthreshold = DFE_URGENTTHRESHOLD;
|
||||
unsigned int dfe_upthreshold = DFE_UPTHRESHOLD;
|
||||
unsigned int dfe_downthreshold = DFE_DOWNTHRESHOLD;
|
||||
unsigned int dfe_idlethreshold = DFE_IDLETHRESHOLD;
|
||||
struct devfreq_simple_exynos_data *data = df->data;
|
||||
unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
|
||||
unsigned long pm_qos_min = 0, cal_qos_max = 0;
|
||||
unsigned long pm_qos_max = 0;
|
||||
unsigned long usage_rate;
|
||||
int err;
|
||||
|
||||
if (data) {
|
||||
if (!df->disabled_pm_qos) {
|
||||
pm_qos_min = pm_qos_request(data->pm_qos_class);
|
||||
if (data->pm_qos_class_max)
|
||||
pm_qos_max = pm_qos_request(data->pm_qos_class_max);
|
||||
if (unlikely(gov_simple_exynos))
|
||||
printk("pm_qos: %lu\n", pm_qos_min);
|
||||
}
|
||||
|
||||
if (data->urgentthreshold)
|
||||
dfe_urgentthreshold = data->urgentthreshold;
|
||||
if (data->upthreshold)
|
||||
dfe_upthreshold = data->upthreshold;
|
||||
if (data->downthreshold)
|
||||
dfe_downthreshold = data->downthreshold;
|
||||
if (data->idlethreshold)
|
||||
dfe_idlethreshold = data->idlethreshold;
|
||||
|
||||
if (data->cal_qos_max) {
|
||||
cal_qos_max = data->cal_qos_max;
|
||||
max = (df->max_freq) ? df->max_freq : 0;
|
||||
} else {
|
||||
cal_qos_max = df->max_freq;
|
||||
}
|
||||
}
|
||||
|
||||
if (df->profile->get_dev_status) {
|
||||
err = df->profile->get_dev_status(df->dev.parent, &stat);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
*freq = pm_qos_min;
|
||||
if (pm_qos_max)
|
||||
*freq = min(pm_qos_max, *freq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assume MAX if it is going to be divided by zero */
|
||||
if (stat.total_time == 0) {
|
||||
*freq = max3(max, cal_qos_max, pm_qos_min);
|
||||
if (pm_qos_max)
|
||||
*freq = min(pm_qos_max, *freq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set MAX if we do not know the initial frequency */
|
||||
if (stat.current_frequency == 0) {
|
||||
*freq = max3(max, cal_qos_max, pm_qos_min);
|
||||
if (pm_qos_max)
|
||||
*freq = min(pm_qos_max, *freq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
usage_rate = div64_u64(stat.busy_time * 100, stat.total_time);
|
||||
|
||||
/* Set MAX if it's busy enough */
|
||||
if (usage_rate > dfe_urgentthreshold)
|
||||
*freq = max3(max, cal_qos_max, pm_qos_min);
|
||||
else if (usage_rate >= dfe_upthreshold) {
|
||||
if (data)
|
||||
*freq = data->above_freq;
|
||||
} else if (usage_rate > dfe_downthreshold)
|
||||
*freq = stat.current_frequency;
|
||||
else if (usage_rate > dfe_idlethreshold) {
|
||||
if (data)
|
||||
*freq = data->below_freq;
|
||||
} else
|
||||
*freq = 0;
|
||||
|
||||
if (*freq > cal_qos_max)
|
||||
*freq = cal_qos_max;
|
||||
|
||||
if (pm_qos_min)
|
||||
*freq = max(pm_qos_min, *freq);
|
||||
|
||||
if (pm_qos_max)
|
||||
*freq = min(pm_qos_max, *freq);
|
||||
|
||||
if (unlikely(gov_simple_exynos))
|
||||
printk("Usage: %lu, freq: %lu, old: %lu\n", usage_rate, *freq, stat.current_frequency);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_simple_exynos_register_notifier(struct devfreq *df)
|
||||
{
|
||||
int ret;
|
||||
struct devfreq_simple_exynos_data *data = df->data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->nb.df = df;
|
||||
data->nb.nb.notifier_call = devfreq_simple_exynos_notifier;
|
||||
|
||||
ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
if (data->pm_qos_class_max) {
|
||||
data->nb_max.df = df;
|
||||
data->nb_max.nb.notifier_call = devfreq_simple_exynos_notifier;
|
||||
|
||||
ret = pm_qos_add_notifier(data->pm_qos_class_max, &data->nb_max.nb);
|
||||
if (ret < 0) {
|
||||
pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
kfree((void *)&data->nb_max.nb);
|
||||
|
||||
err1:
|
||||
kfree((void *)&data->nb.nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int devfreq_simple_exynos_unregister_notifier(struct devfreq *df)
|
||||
{
|
||||
int ret;
|
||||
struct devfreq_simple_exynos_data *data = df->data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->pm_qos_class_max) {
|
||||
ret = pm_qos_remove_notifier(data->pm_qos_class_max, &data->nb_max.nb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int devfreq_simple_exynos_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
ret = devfreq_simple_exynos_register_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
devfreq_monitor_start(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_STOP:
|
||||
devfreq_monitor_stop(devfreq);
|
||||
ret = devfreq_simple_exynos_unregister_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_INTERVAL:
|
||||
devfreq_interval_update(devfreq, (unsigned int *)data);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_SUSPEND:
|
||||
devfreq_monitor_suspend(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_RESUME:
|
||||
devfreq_monitor_resume(devfreq);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_simple_exynos = {
|
||||
.name = "simple_exynos",
|
||||
.get_target_freq = devfreq_simple_exynos_func,
|
||||
.event_handler = devfreq_simple_exynos_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_simple_exynos_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_simple_exynos);
|
||||
}
|
||||
subsys_initcall(devfreq_simple_exynos_init);
|
||||
|
||||
static void __exit devfreq_simple_exynos_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_simple_exynos);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_simple_exynos_exit);
|
||||
MODULE_LICENSE("GPL");
|
273
drivers/devfreq/governor_simpleinteractive.c
Normal file
273
drivers/devfreq/governor_simpleinteractive.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/pm_opp.h>
|
||||
|
||||
#include "governor.h"
|
||||
|
||||
static int devfreq_simple_interactive_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
struct devfreq_notifier_block *devfreq_nb;
|
||||
|
||||
devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
|
||||
|
||||
mutex_lock(&devfreq_nb->df->lock);
|
||||
update_devfreq(devfreq_nb->df);
|
||||
mutex_unlock(&devfreq_nb->df->lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int devfreq_simple_interactive_func(struct devfreq *df,
|
||||
unsigned long *freq)
|
||||
{
|
||||
struct devfreq_simple_interactive_data *data = df->data;
|
||||
unsigned long pm_qos_min = 0;
|
||||
unsigned long pm_qos_max = INT_MAX;
|
||||
int delay_check = 0;
|
||||
int delay_time = 0;
|
||||
int i = 0;
|
||||
struct dev_pm_opp *limit_opp;
|
||||
|
||||
if (!data) {
|
||||
pr_err("%s: failed to find governor data\n", __func__);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
if (!df->disabled_pm_qos) {
|
||||
pm_qos_min = pm_qos_request(data->pm_qos_class);
|
||||
if (data->pm_qos_class_max) {
|
||||
pm_qos_max = pm_qos_request(data->pm_qos_class_max);
|
||||
rcu_read_lock();
|
||||
limit_opp = devfreq_recommended_opp(df->dev.parent, &pm_qos_max,
|
||||
DEVFREQ_FLAG_LEAST_UPPER_BOUND);
|
||||
rcu_read_unlock();
|
||||
if (IS_ERR(limit_opp)) {
|
||||
pr_err("%s: failed to limit by max frequency\n", __func__);
|
||||
return PTR_ERR(limit_opp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*freq = pm_qos_min;
|
||||
|
||||
/* Add load calculation here */
|
||||
|
||||
if (!data->use_delay_time)
|
||||
goto out;
|
||||
|
||||
if (data->prev_freq != df->previous_freq) {
|
||||
for (i = 0; i < data->ndelay_time - 1 &&
|
||||
*freq >= data->delay_time[i + 1]; i += 2)
|
||||
;
|
||||
|
||||
/* unit of delay time should be 10msec */
|
||||
delay_check = data->delay_time[i] % DELAY_TIME_RANGE;
|
||||
delay_time = delay_check ?
|
||||
data->delay_time[i] - delay_check + DELAY_TIME_RANGE :
|
||||
data->delay_time[i];
|
||||
|
||||
data->freq_timer.expires = data->changed_time +
|
||||
msecs_to_jiffies(delay_time);
|
||||
}
|
||||
|
||||
if (pm_qos_max > df->previous_freq && *freq < df->previous_freq &&
|
||||
data->freq_timer.expires > jiffies) {
|
||||
*freq = df->previous_freq;
|
||||
if (!timer_pending(&data->freq_timer))
|
||||
/* timer is bound to cpu0 */
|
||||
add_timer_on(&data->freq_timer, BOUND_CPU_NUM);
|
||||
|
||||
return 0;
|
||||
} else if (timer_pending(&data->freq_timer)) {
|
||||
del_timer_sync(&data->freq_timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* save current frequency and time
|
||||
* to use when update_devfreq is called next
|
||||
*/
|
||||
data->prev_freq = df->previous_freq;
|
||||
data->changed_time = jiffies;
|
||||
|
||||
out:
|
||||
*freq = min(pm_qos_max, *freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_change_freq_task(void *data)
|
||||
{
|
||||
struct devfreq *df = data;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
schedule();
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
update_devfreq(df);
|
||||
mutex_unlock(&df->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*timer callback function send a signal */
|
||||
static void simple_interactive_timer(unsigned long data)
|
||||
{
|
||||
struct devfreq_simple_interactive_data *gov_data = (void *)data;
|
||||
|
||||
wake_up_process(gov_data->change_freq_task);
|
||||
}
|
||||
|
||||
static int devfreq_simple_interactive_register_notifier(struct devfreq *df)
|
||||
{
|
||||
int ret;
|
||||
struct devfreq_simple_interactive_data *data = df->data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->nb.df = df;
|
||||
data->nb.nb.notifier_call = devfreq_simple_interactive_notifier;
|
||||
|
||||
ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
if (data->pm_qos_class_max) {
|
||||
data->nb_max.df = df;
|
||||
data->nb_max.nb.notifier_call = devfreq_simple_interactive_notifier;
|
||||
|
||||
ret = pm_qos_add_notifier(data->pm_qos_class_max, &data->nb_max.nb);
|
||||
if (ret < 0) {
|
||||
pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
|
||||
/* timer of governor for delay time initialize */
|
||||
data->freq_timer.data = (unsigned long)data;
|
||||
data->freq_timer.function = simple_interactive_timer;
|
||||
init_timer(&data->freq_timer);
|
||||
data->change_freq_task = kthread_create(devfreq_change_freq_task, df, "simpleinteractive");
|
||||
|
||||
if (IS_ERR(data->change_freq_task)) {
|
||||
pr_err("%s: failed kthread_create for simpleinteractive governor\n", __func__);
|
||||
ret = PTR_ERR(data->change_freq_task);
|
||||
|
||||
destroy_timer_on_stack(&data->freq_timer);
|
||||
pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
if (data->pm_qos_class_max)
|
||||
pm_qos_remove_notifier(data->pm_qos_class_max, &data->nb_max.nb);
|
||||
|
||||
goto err2;
|
||||
}
|
||||
|
||||
kthread_bind(data->change_freq_task, BOUND_CPU_NUM);
|
||||
wake_up_process(data->change_freq_task);
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
kfree((void *)&data->nb_max.nb);
|
||||
|
||||
err1:
|
||||
kfree((void *)&data->nb.nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int devfreq_simple_interactive_unregister_notifier(struct devfreq *df)
|
||||
{
|
||||
int ret;
|
||||
struct devfreq_simple_interactive_data *data = df->data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->pm_qos_class_max) {
|
||||
ret = pm_qos_remove_notifier(data->pm_qos_class_max, &data->nb_max.nb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
|
||||
destroy_timer_on_stack(&data->freq_timer);
|
||||
kthread_stop(data->change_freq_task);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int devfreq_simple_interactive_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
ret = devfreq_simple_interactive_register_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_STOP:
|
||||
ret = devfreq_simple_interactive_unregister_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_simple_interactive = {
|
||||
.name = "interactive",
|
||||
.get_target_freq = devfreq_simple_interactive_func,
|
||||
.event_handler = devfreq_simple_interactive_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_simple_interactive_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_simple_interactive);
|
||||
}
|
||||
subsys_initcall(devfreq_simple_interactive_init);
|
||||
|
||||
static void __exit devfreq_simple_interactive_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_simple_interactive);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_simple_interactive_exit);
|
||||
MODULE_LICENSE("GPL");
|
238
drivers/devfreq/governor_simpleondemand.c
Normal file
238
drivers/devfreq/governor_simpleondemand.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* linux/drivers/devfreq/governor_simpleondemand.c
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "governor.h"
|
||||
|
||||
static int devfreq_simple_ondemand_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
struct devfreq_notifier_block *devfreq_nb;
|
||||
|
||||
devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
|
||||
|
||||
mutex_lock(&devfreq_nb->df->lock);
|
||||
update_devfreq(devfreq_nb->df);
|
||||
mutex_unlock(&devfreq_nb->df->lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
|
||||
#define DFSO_UPTHRESHOLD (90)
|
||||
#define DFSO_DOWNDIFFERENCTIAL (5)
|
||||
#define DFSO_WEIGHT (100)
|
||||
static int devfreq_simple_ondemand_func(struct devfreq *df,
|
||||
unsigned long *freq)
|
||||
{
|
||||
struct devfreq_dev_status stat;
|
||||
int err;
|
||||
unsigned long long a, b;
|
||||
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
|
||||
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
|
||||
unsigned int dfso_multiplication_weight = DFSO_WEIGHT;
|
||||
struct devfreq_simple_ondemand_data *data = df->data;
|
||||
unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
|
||||
unsigned long pm_qos_min = 0;
|
||||
|
||||
if (data && !df->disabled_pm_qos) {
|
||||
pm_qos_min = pm_qos_request(data->pm_qos_class);
|
||||
if (pm_qos_min >= data->cal_qos_max) {
|
||||
*freq = pm_qos_min;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (df->profile->get_dev_status) {
|
||||
err = df->profile->get_dev_status(df->dev.parent, &stat);
|
||||
} else {
|
||||
*freq = pm_qos_min;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (data) {
|
||||
if (data->upthreshold)
|
||||
dfso_upthreshold = data->upthreshold;
|
||||
if (data->downdifferential)
|
||||
dfso_downdifferential = data->downdifferential;
|
||||
if (data->multiplication_weight)
|
||||
dfso_multiplication_weight = data->multiplication_weight;
|
||||
}
|
||||
if (dfso_upthreshold > 100 ||
|
||||
dfso_upthreshold < dfso_downdifferential)
|
||||
return -EINVAL;
|
||||
|
||||
if (data && data->cal_qos_max)
|
||||
max = (df->max_freq) ? df->max_freq : 0;
|
||||
|
||||
/* Assume MAX if it is going to be divided by zero */
|
||||
if (stat.total_time == 0) {
|
||||
if (data && data->cal_qos_max)
|
||||
max = max3(max, data->cal_qos_max, pm_qos_min);
|
||||
*freq = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prevent overflow */
|
||||
if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) {
|
||||
stat.busy_time >>= 7;
|
||||
stat.total_time >>= 7;
|
||||
}
|
||||
|
||||
stat.busy_time *= dfso_multiplication_weight;
|
||||
stat.busy_time = div64_u64(stat.busy_time, 100);
|
||||
|
||||
/* Set MAX if it's busy enough */
|
||||
if (stat.busy_time * 100 >
|
||||
stat.total_time * dfso_upthreshold) {
|
||||
if (data && data->cal_qos_max)
|
||||
max = max3(max, data->cal_qos_max, pm_qos_min);
|
||||
*freq = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set MAX if we do not know the initial frequency */
|
||||
if (stat.current_frequency == 0) {
|
||||
if (data && data->cal_qos_max)
|
||||
max = max3(max, data->cal_qos_max, pm_qos_min);
|
||||
*freq = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep the current frequency */
|
||||
if (stat.busy_time * 100 >
|
||||
stat.total_time * (dfso_upthreshold - dfso_downdifferential)) {
|
||||
*freq = max(stat.current_frequency, pm_qos_min);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the desired frequency based on the load */
|
||||
a = stat.busy_time;
|
||||
a *= stat.current_frequency;
|
||||
b = div64_u64(a, stat.total_time);
|
||||
b *= 100;
|
||||
b = div64_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
|
||||
|
||||
if (data && data->cal_qos_max) {
|
||||
if (b > data->cal_qos_max)
|
||||
b = data->cal_qos_max;
|
||||
}
|
||||
|
||||
*freq = (unsigned long) b;
|
||||
|
||||
if (pm_qos_min)
|
||||
*freq = max(pm_qos_min, *freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_simple_ondemand_register_notifier(struct devfreq *df)
|
||||
{
|
||||
int ret;
|
||||
struct devfreq_simple_ondemand_data *data = df->data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->nb.df = df;
|
||||
data->nb.nb.notifier_call = devfreq_simple_ondemand_notifier;
|
||||
|
||||
ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree((void *)&data->nb.nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int devfreq_simple_ondemand_unregister_notifier(struct devfreq *df)
|
||||
{
|
||||
struct devfreq_simple_ondemand_data *data = df->data;
|
||||
|
||||
return pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
}
|
||||
|
||||
static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
ret = devfreq_simple_ondemand_register_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
devfreq_monitor_start(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_STOP:
|
||||
devfreq_monitor_stop(devfreq);
|
||||
ret = devfreq_simple_ondemand_unregister_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_INTERVAL:
|
||||
devfreq_interval_update(devfreq, (unsigned int *)data);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_SUSPEND:
|
||||
devfreq_monitor_suspend(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_RESUME:
|
||||
devfreq_monitor_resume(devfreq);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_simple_ondemand = {
|
||||
.name = "simple_ondemand",
|
||||
.get_target_freq = devfreq_simple_ondemand_func,
|
||||
.event_handler = devfreq_simple_ondemand_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_simple_ondemand_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_simple_ondemand);
|
||||
}
|
||||
subsys_initcall(devfreq_simple_ondemand_init);
|
||||
|
||||
static void __exit devfreq_simple_ondemand_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_simple_ondemand);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_simple_ondemand_exit);
|
||||
MODULE_LICENSE("GPL");
|
194
drivers/devfreq/governor_simpleusage.c
Normal file
194
drivers/devfreq/governor_simpleusage.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "governor.h"
|
||||
|
||||
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
|
||||
#define DFSO_UPTHRESHOLD (50)
|
||||
#define DFSO_TARGET_PERCENTAGE (20)
|
||||
#define DFSO_PROPORTIONAL (120)
|
||||
#define DFSO_WEIGHT (100)
|
||||
|
||||
static int devfreq_simple_usage_notifier(struct notifier_block *nb, unsigned long val, void *data)
|
||||
{
|
||||
struct devfreq_notifier_block *devfreq_nb;
|
||||
struct devfreq_simple_usage_data *simple_usage_data;
|
||||
|
||||
devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
|
||||
simple_usage_data = container_of(devfreq_nb, struct devfreq_simple_usage_data, nb);
|
||||
devfreq_nb = &simple_usage_data->nb;
|
||||
|
||||
mutex_lock(&devfreq_nb->df->lock);
|
||||
update_devfreq(devfreq_nb->df);
|
||||
mutex_unlock(&devfreq_nb->df->lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int devfreq_simple_usage_func(struct devfreq *df, unsigned long *freq)
|
||||
{
|
||||
struct devfreq_dev_status stat;
|
||||
int err = df->profile->get_dev_status(df->dev.parent, &stat);
|
||||
unsigned long long a, b;
|
||||
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
|
||||
unsigned int dfso_target_percentage = DFSO_TARGET_PERCENTAGE;
|
||||
unsigned int dfso_proportional = DFSO_PROPORTIONAL;
|
||||
unsigned int dfso_multiplication_weight = DFSO_WEIGHT;
|
||||
struct devfreq_simple_usage_data *data = df->data;
|
||||
unsigned long max = (df->max_freq) ? df->max_freq : 0;
|
||||
unsigned long pm_qos_min;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
if (!df->disabled_pm_qos)
|
||||
pm_qos_min = pm_qos_request(data->pm_qos_class);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (data->upthreshold)
|
||||
dfso_upthreshold = data->upthreshold;
|
||||
if (data->target_percentage)
|
||||
dfso_target_percentage = data->target_percentage;
|
||||
if (data->proportional)
|
||||
dfso_proportional = data->proportional;
|
||||
if (data->multiplication_weight)
|
||||
dfso_multiplication_weight = data->multiplication_weight;
|
||||
|
||||
a = stat.busy_time * dfso_multiplication_weight;
|
||||
a = div64_u64(a, 100);
|
||||
a = a * dfso_proportional;
|
||||
b = div64_u64(a, stat.total_time);
|
||||
|
||||
/* If percentage is larger than upthreshold, set with max freq */
|
||||
if (b >= data->upthreshold) {
|
||||
max = max(data->cal_qos_max, pm_qos_min);
|
||||
*freq = max;
|
||||
|
||||
if (*freq > df->max_freq)
|
||||
*freq = df->max_freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
b *= stat.current_frequency;
|
||||
|
||||
a = div64_u64(b, dfso_target_percentage);
|
||||
|
||||
if (a > data->cal_qos_max)
|
||||
a = data->cal_qos_max;
|
||||
|
||||
*freq = (unsigned long) a;
|
||||
|
||||
if (pm_qos_min && *freq < pm_qos_min)
|
||||
*freq = pm_qos_min;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devfreq_simple_usage_register_notifier(struct devfreq *df)
|
||||
{
|
||||
int ret;
|
||||
struct devfreq_simple_usage_data *data = df->data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->nb.df = df;
|
||||
data->nb.nb.notifier_call = devfreq_simple_usage_notifier;
|
||||
|
||||
ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree((void *)&data->nb.nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int devfreq_simple_usage_unregister_notifier(struct devfreq *df)
|
||||
{
|
||||
struct devfreq_simple_usage_data *data = df->data;
|
||||
|
||||
return pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
|
||||
}
|
||||
|
||||
static int devfreq_simple_usage_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
ret = devfreq_simple_usage_register_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
devfreq_monitor_start(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_STOP:
|
||||
devfreq_monitor_stop(devfreq);
|
||||
ret = devfreq_simple_usage_unregister_notifier(devfreq);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_INTERVAL:
|
||||
devfreq_interval_update(devfreq, (unsigned int*)data);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_SUSPEND:
|
||||
devfreq_monitor_suspend(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_RESUME:
|
||||
devfreq_monitor_resume(devfreq);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_simple_usage = {
|
||||
.name = "simple_usage",
|
||||
.get_target_freq = devfreq_simple_usage_func,
|
||||
.event_handler = devfreq_simple_usage_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_simple_usage_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_simple_usage);
|
||||
}
|
||||
subsys_initcall(devfreq_simple_usage_init);
|
||||
|
||||
static void __exit devfreq_simple_usage_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_simple_usage);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_simple_usage_exit);
|
||||
MODULE_LICENSE("GPL");
|
162
drivers/devfreq/governor_userspace.c
Normal file
162
drivers/devfreq/governor_userspace.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* linux/drivers/devfreq/governor_simpleondemand.c
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include "governor.h"
|
||||
|
||||
struct userspace_data {
|
||||
unsigned long user_frequency;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
|
||||
{
|
||||
struct userspace_data *data = df->data;
|
||||
|
||||
if (data->valid) {
|
||||
unsigned long adjusted_freq = data->user_frequency;
|
||||
|
||||
if (df->max_freq && adjusted_freq > df->max_freq)
|
||||
adjusted_freq = df->max_freq;
|
||||
|
||||
if (df->min_freq && adjusted_freq < df->min_freq)
|
||||
adjusted_freq = df->min_freq;
|
||||
|
||||
*freq = adjusted_freq;
|
||||
} else {
|
||||
*freq = df->previous_freq; /* No user freq specified yet */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t store_freq(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
struct userspace_data *data;
|
||||
unsigned long wanted;
|
||||
int err = 0;
|
||||
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
data = devfreq->data;
|
||||
|
||||
sscanf(buf, "%lu", &wanted);
|
||||
data->user_frequency = wanted;
|
||||
data->valid = true;
|
||||
err = update_devfreq(devfreq);
|
||||
if (err == 0)
|
||||
err = count;
|
||||
mutex_unlock(&devfreq->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t show_freq(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
struct userspace_data *data;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
data = devfreq->data;
|
||||
|
||||
if (data->valid)
|
||||
err = sprintf(buf, "%lu\n", data->user_frequency);
|
||||
else
|
||||
err = sprintf(buf, "undefined\n");
|
||||
mutex_unlock(&devfreq->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq);
|
||||
static struct attribute *dev_entries[] = {
|
||||
&dev_attr_set_freq.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dev_attr_group = {
|
||||
.name = "userspace",
|
||||
.attrs = dev_entries,
|
||||
};
|
||||
|
||||
static int userspace_init(struct devfreq *devfreq)
|
||||
{
|
||||
int err = 0;
|
||||
struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!data) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
data->valid = false;
|
||||
devfreq->data = data;
|
||||
|
||||
err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void userspace_exit(struct devfreq *devfreq)
|
||||
{
|
||||
sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
|
||||
kfree(devfreq->data);
|
||||
devfreq->data = NULL;
|
||||
}
|
||||
|
||||
static int devfreq_userspace_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
ret = userspace_init(devfreq);
|
||||
break;
|
||||
case DEVFREQ_GOV_STOP:
|
||||
userspace_exit(devfreq);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_userspace = {
|
||||
.name = "userspace",
|
||||
.get_target_freq = devfreq_userspace_func,
|
||||
.event_handler = devfreq_userspace_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_userspace_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_userspace);
|
||||
}
|
||||
subsys_initcall(devfreq_userspace_init);
|
||||
|
||||
static void __exit devfreq_userspace_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_userspace);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_userspace_exit);
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue