/* drivers/devfreq/exynos4210_memorybus.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* EXYNOS4 - Memory/Bus clock frequency scaling support in DEVFREQ framework
* This version supports EXYNOS4210 only. This changes bus frequencies
* and vddint voltages. Exynos4412/4212 should be able to be supported
* with minor modifications.
*
* 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/io.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/suspend.h>
#include <linux/opp.h>
#include <linux/devfreq.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
/* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */
#ifdef CONFIG_EXYNOS_ASV
extern unsigned int exynos_result_of_asv;
#endif
#include <mach/regs-clock.h>
#include <plat/map-s5p.h>
#define MAX_SAFEVOLT 1200000 /* 1.2V */
enum exynos4_busf_type {
TYPE_BUSF_EXYNOS4210,
TYPE_BUSF_EXYNOS4x12,
};
/* Assume that the bus is saturated if the utilization is 40% */
#define BUS_SATURATION_RATIO 40
enum ppmu_counter {
PPMU_PMNCNT0 = 0,
PPMU_PMCCNT1,
PPMU_PMNCNT2,
PPMU_PMNCNT3,
PPMU_PMNCNT_MAX,
};
struct exynos4_ppmu {
void __iomem *hw_base;
unsigned int ccnt;
unsigned int event;
unsigned int count[PPMU_PMNCNT_MAX];
bool ccnt_overflow;
bool count_overflow[PPMU_PMNCNT_MAX];
};
enum busclk_level_idx {
LV_0 = 0,
LV_1,
LV_2,
LV_3,
LV_4,
_LV_END
};
#define EX4210_LV_MAX LV_2
#define EX4x12_LV_MAX LV_4
#define EX4210_LV_NUM (LV_2 + 1)
#define EX4x12_LV_NUM (LV_4 + 1)
/**
* struct busfreq_opp_info - opp information for bus
* @rate: Frequency in hertz
* @volt: Voltage in microvolts corresponding to this OPP
*/
struct busfreq_opp_info {
unsigned long rate;
unsigned long volt;
};
struct busfreq_data {
enum exynos4_busf_type type;
struct device *dev;
struct devfreq *devfreq;
bool disabled;
struct regulator *vdd_int;
struct regulator *vdd_mif; /* Exynos4412/4212 only */
struct busfreq_opp_info curr_oppinfo;
struct exynos4_ppmu dmc[2];
struct notifier_block pm_notifier;
struct mutex lock;
/* Dividers calculated at boot/probe-time */
unsigned int dmc_divtable[_LV_END]; /* DMC0 */
unsigned int top_divtable[_LV_END];
};
struct bus_opp_table {
unsigned int idx;
unsigned long clk;
unsigned long volt;
};
/* 4210 controls clock of mif and voltage of int */
static struct bus_opp_table exynos4210_busclk_table[] = {
{LV_0, 400000, 1150000},
{LV_1, 267000, 1050000},
{LV_2, 133000, 1025000},
{0, 0, 0},
};
/*
* MIF is the main control knob clock for exynox4x12 MIF/INT
* clock and voltage of both mif/int are controlled.
*/
static struct bus_opp_table exynos4x12_mifclk_table[] = {
{LV_0, 400000, 1100000},
{LV_1, 267000, 1000000},
{LV_2, 160000, 950000},
{LV_3, 133000, 950000},
{LV_4, 100000, 950000},
{0, 0, 0},
};
/*
* INT is not the control knob of 4x12. LV_x is not meant to represent
* the current performance. (MIF does)
*/
static struct bus_opp_table exynos4x12_intclk_table[] = {
{LV_0, 200000, 1000000},
{LV_1, 160000, 950000},
{LV_2, 133000, 925000},
{LV_3, 100000, 900000},
{0, 0, 0},
};
/* TODO: asv volt definitions are "__initdata"? */
/* Some chips have different operating voltages */
static unsigned int exynos4210_asv_volt[][EX4210_LV_NUM] = {
{1150000, 1050000, 1050000},
{1125000, 1025000, 1025000},
{1100000, 1000000, 1000000},
{1075000, 975000, 975000},