Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6392bfd5 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge tag 'for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq into pm-devfreq

Pull devfreq updates for v3.16 from MyungJoo Ham.

 - Clean up with modern macro in the core and drivers.
 - Fix incorrect error returns
 - Remove dead CONFIG check.
 - Fix resource leak in a driver.

* tag 'for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq:
  PM / devfreq: remove checks for CONFIG_EXYNOS_ASV
  PM / devfreq: exynos5: Use devm_devfreq_* function using device resource management
  PM / devfreq: exynos4: Use devm_devfreq_* function using device resource management
  PM / devfreq: Add devm_devfreq_{register,unregister}_opp_notfier function
  PM / devfreq: Add resource-managed function for devfreq device
  PM / devfreq: Fix devfreq_remove_device() to improve the sequence of resource free
  PM / devfreq: exynos: make more PPMU code common
  PM / devfreq: exynos5: introduce struct busfreq_ppmu_data
  PM / devfreq: exynos4: introduce struct busfreq_ppmu_data
  PM / devfreq: exynos4: use common PPMU code
  PM / devfreq: exynos5: Add CONFIG_PM_OPP dependency to fix probe fail
  PM / devfreq: exynos5: Use SIMPLE_DEV_PM_OPS macro
  PM / devfreq: exynos4: Add CONFIG_PM_OPP dependency to fix probe fail
  PM / devfreq: exynos4: Use SIMPLE_DEV_PM_OPS macro
  PM / devfreq: exynos4: Fix bug of resource leak and code clean on probe()
parents c7208164 cb7063f4
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -70,19 +70,20 @@ config ARM_EXYNOS4_BUS_DEVFREQ
	depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
	select ARCH_HAS_OPP
	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.
	  To operate with optimal voltages, ASV support is required
	  (CONFIG_EXYNOS_ASV).
	  This does not yet operate with optimal voltages.

config ARM_EXYNOS5_BUS_DEVFREQ
	bool "ARM Exynos5250 Bus DEVFREQ Driver"
	depends on SOC_EXYNOS5250
	select ARCH_HAS_OPP
	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
+115 −10
Original line number Diff line number Diff line
@@ -394,7 +394,7 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
 * @devfreq:	the devfreq struct
 * @skip:	skip calling device_unregister().
 */
static void _remove_devfreq(struct devfreq *devfreq, bool skip)
static void _remove_devfreq(struct devfreq *devfreq)
{
	mutex_lock(&devfreq_list_lock);
	if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
@@ -412,11 +412,6 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
	if (devfreq->profile->exit)
		devfreq->profile->exit(devfreq->dev.parent);

	if (!skip && get_device(&devfreq->dev)) {
		device_unregister(&devfreq->dev);
		put_device(&devfreq->dev);
	}

	mutex_destroy(&devfreq->lock);
	kfree(devfreq);
}
@@ -426,14 +421,12 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
 * @dev:	the devfreq device
 *
 * This calls _remove_devfreq() if _remove_devfreq() is not called.
 * Note that devfreq_dev_release() could be called by _remove_devfreq() as
 * well as by others unregistering the device.
 */
static void devfreq_dev_release(struct device *dev)
{
	struct devfreq *devfreq = to_devfreq(dev);

	_remove_devfreq(devfreq, true);
	_remove_devfreq(devfreq);
}

/**
@@ -544,12 +537,76 @@ int devfreq_remove_device(struct devfreq *devfreq)
	if (!devfreq)
		return -EINVAL;

	_remove_devfreq(devfreq, false);
	device_unregister(&devfreq->dev);
	put_device(&devfreq->dev);

	return 0;
}
EXPORT_SYMBOL(devfreq_remove_device);

static int devm_devfreq_dev_match(struct device *dev, void *res, void *data)
{
	struct devfreq **r = res;

	if (WARN_ON(!r || !*r))
		return 0;

	return *r == data;
}

static void devm_devfreq_dev_release(struct device *dev, void *res)
{
	devfreq_remove_device(*(struct devfreq **)res);
}

/**
 * devm_devfreq_add_device() - Resource-managed devfreq_add_device()
 * @dev:	the device to add devfreq feature.
 * @profile:	device-specific profile to run devfreq.
 * @governor_name:	name of the policy to choose frequency.
 * @data:	private data for the governor. The devfreq framework does not
 *		touch this value.
 *
 * This function manages automatically the memory of devfreq device using device
 * resource management and simplify the free operation for memory of devfreq
 * device.
 */
struct devfreq *devm_devfreq_add_device(struct device *dev,
					struct devfreq_dev_profile *profile,
					const char *governor_name,
					void *data)
{
	struct devfreq **ptr, *devfreq;

	ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	devfreq = devfreq_add_device(dev, profile, governor_name, data);
	if (IS_ERR(devfreq)) {
		devres_free(ptr);
		return ERR_PTR(-ENOMEM);
	}

	*ptr = devfreq;
	devres_add(dev, ptr);

	return devfreq;
}
EXPORT_SYMBOL(devm_devfreq_add_device);

/**
 * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device()
 * @dev:	the device to add devfreq feature.
 * @devfreq:	the devfreq instance to be removed
 */
void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq)
{
	WARN_ON(devres_release(dev, devm_devfreq_dev_release,
			       devm_devfreq_dev_match, devfreq));
}
EXPORT_SYMBOL(devm_devfreq_remove_device);

/**
 * devfreq_suspend_device() - Suspend devfreq of a device.
 * @devfreq: the devfreq instance to be suspended
@@ -1112,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
	return ret;
}

static void devm_devfreq_opp_release(struct device *dev, void *res)
{
	devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res);
}

/**
 * devm_ devfreq_register_opp_notifier()
 *		- Resource-managed devfreq_register_opp_notifier()
 * @dev:	The devfreq user device. (parent of devfreq)
 * @devfreq:	The devfreq object.
 */
int devm_devfreq_register_opp_notifier(struct device *dev,
				       struct devfreq *devfreq)
{
	struct devfreq **ptr;
	int ret;

	ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;

	ret = devfreq_register_opp_notifier(dev, devfreq);
	if (ret) {
		devres_free(ptr);
		return ret;
	}

	*ptr = devfreq;
	devres_add(dev, ptr);

	return 0;
}
EXPORT_SYMBOL(devm_devfreq_register_opp_notifier);

/**
 * devm_devfreq_unregister_opp_notifier()
 *		- Resource-managed devfreq_unregister_opp_notifier()
 * @dev:	The devfreq user device. (parent of devfreq)
 * @devfreq:	The devfreq object.
 */
void devm_devfreq_unregister_opp_notifier(struct device *dev,
					 struct devfreq *devfreq)
{
	WARN_ON(devres_release(dev, devm_devfreq_opp_release,
			       devm_devfreq_dev_match, devfreq));
}
EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);

MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_DESCRIPTION("devfreq class support");
MODULE_LICENSE("GPL");
+1 −1
Original line number Diff line number Diff line
# Exynos DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ)	+= exynos4_bus.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
+60 −159
Original line number Diff line number Diff line
@@ -25,13 +25,9 @@
#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/map.h>

#include "exynos_ppmu.h"
#include "exynos4_bus.h"

#define MAX_SAFEVOLT	1200000 /* 1.2V */
@@ -44,22 +40,6 @@ enum exynos4_busf_type {
/* 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,
@@ -68,6 +48,13 @@ enum busclk_level_idx {
	LV_4,
	_LV_END
};

enum exynos_ppmu_idx {
	PPMU_DMC0,
	PPMU_DMC1,
	PPMU_END,
};

#define EX4210_LV_MAX	LV_2
#define EX4x12_LV_MAX	LV_4
#define EX4210_LV_NUM	(LV_2 + 1)
@@ -91,7 +78,7 @@ struct busfreq_data {
	struct regulator *vdd_int;
	struct regulator *vdd_mif; /* Exynos4412/4212 only */
	struct busfreq_opp_info curr_oppinfo;
	struct exynos4_ppmu dmc[2];
	struct busfreq_ppmu_data ppmu_data;

	struct notifier_block pm_notifier;
	struct mutex lock;
@@ -101,12 +88,6 @@ struct busfreq_data {
	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},
@@ -524,57 +505,6 @@ static int exynos4x12_set_busclk(struct busfreq_data *data,
	return 0;
}


static void busfreq_mon_reset(struct busfreq_data *data)
{
	unsigned int i;

	for (i = 0; i < 2; i++) {
		void __iomem *ppmu_base = data->dmc[i].hw_base;

		/* Reset PPMU */
		__raw_writel(0x8000000f, ppmu_base + 0xf010);
		__raw_writel(0x8000000f, ppmu_base + 0xf050);
		__raw_writel(0x6, ppmu_base + 0xf000);
		__raw_writel(0x0, ppmu_base + 0xf100);

		/* Set PPMU Event */
		data->dmc[i].event = 0x6;
		__raw_writel(((data->dmc[i].event << 12) | 0x1),
			     ppmu_base + 0xfc);

		/* Start PPMU */
		__raw_writel(0x1, ppmu_base + 0xf000);
	}
}

static void exynos4_read_ppmu(struct busfreq_data *data)
{
	int i, j;

	for (i = 0; i < 2; i++) {
		void __iomem *ppmu_base = data->dmc[i].hw_base;
		u32 overflow;

		/* Stop PPMU */
		__raw_writel(0x0, ppmu_base + 0xf000);

		/* Update local data from PPMU */
		overflow = __raw_readl(ppmu_base + 0xf050);

		data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100);
		data->dmc[i].ccnt_overflow = overflow & (1 << 31);

		for (j = 0; j < PPMU_PMNCNT_MAX; j++) {
			data->dmc[i].count[j] = __raw_readl(
					ppmu_base + (0xf110 + (0x10 * j)));
			data->dmc[i].count_overflow[j] = overflow & (1 << j);
		}
	}

	busfreq_mon_reset(data);
}

static int exynos4x12_get_intspec(unsigned long mifclk)
{
	int i = 0;
@@ -698,84 +628,35 @@ out:
	return err;
}

static int exynos4_get_busier_dmc(struct busfreq_data *data)
{
	u64 p0 = data->dmc[0].count[0];
	u64 p1 = data->dmc[1].count[0];

	p0 *= data->dmc[1].ccnt;
	p1 *= data->dmc[0].ccnt;

	if (data->dmc[1].ccnt == 0)
		return 0;

	if (p0 > p1)
		return 0;
	return 1;
}

static int exynos4_bus_get_dev_status(struct device *dev,
				      struct devfreq_dev_status *stat)
{
	struct busfreq_data *data = dev_get_drvdata(dev);
	int busier_dmc;
	int cycles_x2 = 2; /* 2 x cycles */
	void __iomem *addr;
	u32 timing;
	u32 memctrl;

	exynos4_read_ppmu(data);
	busier_dmc = exynos4_get_busier_dmc(data);
	stat->current_frequency = data->curr_oppinfo.rate;
	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
	int busier;

	if (busier_dmc)
		addr = S5P_VA_DMC1;
	else
		addr = S5P_VA_DMC0;

	memctrl = __raw_readl(addr + 0x04); /* one of DDR2/3/LPDDR2 */
	timing = __raw_readl(addr + 0x38); /* CL or WL/RL values */

	switch ((memctrl >> 8) & 0xf) {
	case 0x4: /* DDR2 */
		cycles_x2 = ((timing >> 16) & 0xf) * 2;
		break;
	case 0x5: /* LPDDR2 */
	case 0x6: /* DDR3 */
		cycles_x2 = ((timing >> 8) & 0xf) + ((timing >> 0) & 0xf);
		break;
	default:
		pr_err("%s: Unknown Memory Type(%d).\n", __func__,
		       (memctrl >> 8) & 0xf);
		return -EINVAL;
	}
	exynos_read_ppmu(ppmu_data);
	busier = exynos_get_busier_ppmu(ppmu_data);
	stat->current_frequency = data->curr_oppinfo.rate;

	/* Number of cycles spent on memory access */
	stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2);
	stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3];
	stat->busy_time *= 100 / BUS_SATURATION_RATIO;
	stat->total_time = data->dmc[busier_dmc].ccnt;
	stat->total_time = ppmu_data->ppmu[busier].ccnt;

	/* If the counters have overflown, retry */
	if (data->dmc[busier_dmc].ccnt_overflow ||
	    data->dmc[busier_dmc].count_overflow[0])
	if (ppmu_data->ppmu[busier].ccnt_overflow ||
	    ppmu_data->ppmu[busier].count_overflow[0])
		return -EAGAIN;

	return 0;
}

static void exynos4_bus_exit(struct device *dev)
{
	struct busfreq_data *data = dev_get_drvdata(dev);

	devfreq_unregister_opp_notifier(dev, data->devfreq);
}

static struct devfreq_dev_profile exynos4_devfreq_profile = {
	.initial_freq	= 400000,
	.polling_ms	= 50,
	.target		= exynos4_bus_target,
	.get_dev_status	= exynos4_bus_get_dev_status,
	.exit		= exynos4_bus_exit,
};

static int exynos4210_init_tables(struct busfreq_data *data)
@@ -837,11 +718,11 @@ static int exynos4210_init_tables(struct busfreq_data *data)
		data->top_divtable[i] = tmp;
	}

#ifdef CONFIG_EXYNOS_ASV
	tmp = exynos4_result_of_asv;
#else
	/*
	 * TODO: init tmp based on busfreq_data
	 * (device-tree or platform-data)
	 */
	tmp = 0; /* Max voltages for the reliability of the unknown */
#endif

	pr_debug("ASV Group of Exynos4 is %d\n", tmp);
	/* Use merged grouping for voltage */
@@ -922,11 +803,7 @@ static int exynos4x12_init_tables(struct busfreq_data *data)
		data->dmc_divtable[i] = tmp;
	}

#ifdef CONFIG_EXYNOS_ASV
	tmp = exynos4_result_of_asv;
#else
	tmp = 0; /* Max voltages for the reliability of the unknown */
#endif

	if (tmp > 8)
		tmp = 0;
@@ -1020,6 +897,7 @@ unlock:
static int exynos4_busfreq_probe(struct platform_device *pdev)
{
	struct busfreq_data *data;
	struct busfreq_ppmu_data *ppmu_data;
	struct dev_pm_opp *opp;
	struct device *dev = &pdev->dev;
	int err = 0;
@@ -1030,9 +908,19 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
		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;
	}

	data->type = pdev->id_entry->driver_data;
	data->dmc[0].hw_base = S5P_VA_DMC0;
	data->dmc[1].hw_base = S5P_VA_DMC1;
	ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0;
	ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1;
	data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event;
	data->dev = dev;
	mutex_init(&data->lock);
@@ -1048,8 +936,11 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
		dev_err(dev, "Cannot determine the device id %d\n", data->type);
		err = -EINVAL;
	}
	if (err)
	if (err) {
		dev_err(dev, "Cannot initialize busfreq table %d\n",
			     data->type);
		return err;
	}

	data->vdd_int = devm_regulator_get(dev, "vdd_int");
	if (IS_ERR(data->vdd_int)) {
@@ -1079,19 +970,28 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, data);

	busfreq_mon_reset(data);

	data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile,
	data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile,
					   "simple_ondemand", NULL);
	if (IS_ERR(data->devfreq))
		return PTR_ERR(data->devfreq);

	devfreq_register_opp_notifier(dev, data->devfreq);
	/*
	 * Start PPMU (Performance Profiling Monitoring Unit) to check
	 * utilization of each IP in the Exynos4 SoC.
	 */
	busfreq_mon_reset(ppmu_data);

	/* Register opp_notifier for Exynos4 busfreq */
	err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
	if (err < 0) {
		dev_err(dev, "Failed to register opp notifier\n");
		return err;
	}

	/* Register pm_notifier for Exynos4 busfreq */
	err = register_pm_notifier(&data->pm_notifier);
	if (err) {
		dev_err(dev, "Failed to setup pm notifier\n");
		devfreq_remove_device(data->devfreq);
		return err;
	}

@@ -1102,23 +1002,24 @@ static int exynos4_busfreq_remove(struct platform_device *pdev)
{
	struct busfreq_data *data = platform_get_drvdata(pdev);

	/* Unregister all of notifier chain */
	unregister_pm_notifier(&data->pm_notifier);
	devfreq_remove_device(data->devfreq);

	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int exynos4_busfreq_resume(struct device *dev)
{
	struct busfreq_data *data = dev_get_drvdata(dev);
	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;

	busfreq_mon_reset(data);
	busfreq_mon_reset(ppmu_data);
	return 0;
}
#endif

static const struct dev_pm_ops exynos4_busfreq_pm = {
	.resume	= exynos4_busfreq_resume,
};
static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume);

static const struct platform_device_id exynos4_busfreq_id[] = {
	{ "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 },
@@ -1134,7 +1035,7 @@ static struct platform_driver exynos4_busfreq_driver = {
	.driver = {
		.name	= "exynos4-busfreq",
		.owner	= THIS_MODULE,
		.pm	= &exynos4_busfreq_pm,
		.pm	= &exynos4_busfreq_pm_ops,
	},
};

+36 −94
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ struct busfreq_data_int {
	struct device *dev;
	struct devfreq *devfreq;
	struct regulator *vdd_int;
	struct exynos_ppmu ppmu[PPMU_END];
	struct busfreq_ppmu_data ppmu_data;
	unsigned long curr_freq;
	bool disabled;

@@ -75,49 +75,6 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
	{0, 0, 0},
};

static void busfreq_mon_reset(struct busfreq_data_int *data)
{
	unsigned int i;

	for (i = PPMU_RIGHT; i < PPMU_END; i++) {
		void __iomem *ppmu_base = data->ppmu[i].hw_base;

		/* Reset the performance and cycle counters */
		exynos_ppmu_reset(ppmu_base);

		/* Setup count registers to monitor read/write transactions */
		data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
		exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
					data->ppmu[i].event[PPMU_PMNCNT3]);

		exynos_ppmu_start(ppmu_base);
	}
}

static void exynos5_read_ppmu(struct busfreq_data_int *data)
{
	int i, j;

	for (i = PPMU_RIGHT; i < PPMU_END; i++) {
		void __iomem *ppmu_base = data->ppmu[i].hw_base;

		exynos_ppmu_stop(ppmu_base);

		/* Update local data from PPMU */
		data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT);

		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
			if (data->ppmu[i].event[j] == 0)
				data->ppmu[i].count[j] = 0;
			else
				data->ppmu[i].count[j] =
					exynos_ppmu_read(ppmu_base, j);
		}
	}

	busfreq_mon_reset(data);
}

static int exynos5_int_setvolt(struct busfreq_data_int *data,
				unsigned long volt)
{
@@ -185,59 +142,33 @@ out:
	return err;
}

static int exynos5_get_busier_dmc(struct busfreq_data_int *data)
{
	int i, j;
	int busy = 0;
	unsigned int temp = 0;

	for (i = PPMU_RIGHT; i < PPMU_END; i++) {
		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
			if (data->ppmu[i].count[j] > temp) {
				temp = data->ppmu[i].count[j];
				busy = i;
			}
		}
	}

	return busy;
}

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;

	exynos5_read_ppmu(data);
	busier_dmc = exynos5_get_busier_dmc(data);
	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 = data->ppmu[busier_dmc].count[PPMU_PMNCNT3];
	stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3];
	stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO;
	stat->total_time = data->ppmu[busier_dmc].ccnt;
	stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt;

	return 0;
}
static void exynos5_int_exit(struct device *dev)
{
	struct platform_device *pdev = container_of(dev, struct platform_device,
						    dev);
	struct busfreq_data_int *data = platform_get_drvdata(pdev);

	devfreq_unregister_opp_notifier(dev, data->devfreq);
}

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,
	.exit			= exynos5_int_exit,
};

static int exynos5250_init_int_tables(struct busfreq_data_int *data)
@@ -315,6 +246,7 @@ unlock:
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;
@@ -330,16 +262,26 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
		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 = PPMU_RIGHT; i < PPMU_END; i++) {
	for (i = 0; i < ppmu_data->ppmu_end; i++) {
		/* map PPMU memory region */
		data->ppmu[i].hw_base = of_iomap(np, i);
		if (data->ppmu[i].hw_base == NULL) {
		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;
		}
@@ -390,32 +332,29 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, data);

	busfreq_mon_reset(data);
	busfreq_mon_reset(ppmu_data);

	data->devfreq = devfreq_add_device(dev, &exynos5_devfreq_int_profile,
	data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile,
					   "simple_ondemand", NULL);
	if (IS_ERR(data->devfreq))
		return PTR_ERR(data->devfreq);

	if (IS_ERR(data->devfreq)) {
		err = PTR_ERR(data->devfreq);
		goto err_devfreq_add;
	err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
	if (err < 0) {
		dev_err(dev, "Failed to register opp notifier\n");
		return err;
	}

	devfreq_register_opp_notifier(dev, data->devfreq);

	err = register_pm_notifier(&data->pm_notifier);
	if (err) {
		dev_err(dev, "Failed to setup pm notifier\n");
		goto err_devfreq_add;
		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;

err_devfreq_add:
	devfreq_remove_device(data->devfreq);
	return err;
}

static int exynos5_busfreq_int_remove(struct platform_device *pdev)
@@ -424,24 +363,27 @@ static int exynos5_busfreq_int_remove(struct platform_device *pdev)

	pm_qos_remove_request(&data->int_req);
	unregister_pm_notifier(&data->pm_notifier);
	devfreq_remove_device(data->devfreq);

	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(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;
@@ -452,7 +394,7 @@ static struct platform_driver exynos5_busfreq_int_driver = {
	.driver		= {
		.name		= "exynos5-bus-int",
		.owner		= THIS_MODULE,
		.pm		= &exynos5_busfreq_int_pm,
		.pm		= &exynos5_busfreq_int_pm_ops,
	},
};

Loading