mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
PM / devfreq: exynos: Remove unused exynos4/5 busfreq driver
This patch removes the unused exynos4/5 busfreq driver. Instead, generic exynos-bus frequency driver support the all Exynos SoCs. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Acked-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
This commit is contained in:
parent
38279cac36
commit
bfcd620487
@ -90,28 +90,6 @@ config ARM_EXYNOS_BUS_DEVFREQ
|
||||
and adjusts the operating frequencies and voltages with OPP support.
|
||||
This does not yet operate with optimal voltages.
|
||||
|
||||
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_TEGRA_DEVFREQ
|
||||
tristate "Tegra DEVFREQ Driver"
|
||||
depends on ARCH_TEGRA_124_SOC
|
||||
|
@ -8,8 +8,6 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
|
||||
|
||||
# DEVFREQ Drivers
|
||||
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
|
||||
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/
|
||||
obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/
|
||||
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
|
||||
|
||||
# DEVFREQ Event Drivers
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Exynos DEVFREQ Drivers
|
||||
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o
|
||||
obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o
|
File diff suppressed because it is too large
Load Diff
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,431 +0,0 @@
|
||||
/*
|
||||
* 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",
|
||||
.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");
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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);
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
Loading…
Reference in New Issue
Block a user