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 Original line Diff line number Diff line
@@ -70,19 +70,20 @@ config ARM_EXYNOS4_BUS_DEVFREQ
	depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
	depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
	select ARCH_HAS_OPP
	select ARCH_HAS_OPP
	select DEVFREQ_GOV_SIMPLE_ONDEMAND
	select DEVFREQ_GOV_SIMPLE_ONDEMAND
	select PM_OPP
	help
	help
	  This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int)
	  This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int)
	  and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int).
	  and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int).
	  It reads PPMU counters of memory controllers and adjusts
	  It reads PPMU counters of memory controllers and adjusts
	  the operating frequencies and voltages with OPP support.
	  the operating frequencies and voltages with OPP support.
	  To operate with optimal voltages, ASV support is required
	  This does not yet operate with optimal voltages.
	  (CONFIG_EXYNOS_ASV).


config ARM_EXYNOS5_BUS_DEVFREQ
config ARM_EXYNOS5_BUS_DEVFREQ
	bool "ARM Exynos5250 Bus DEVFREQ Driver"
	bool "ARM Exynos5250 Bus DEVFREQ Driver"
	depends on SOC_EXYNOS5250
	depends on SOC_EXYNOS5250
	select ARCH_HAS_OPP
	select ARCH_HAS_OPP
	select DEVFREQ_GOV_SIMPLE_ONDEMAND
	select DEVFREQ_GOV_SIMPLE_ONDEMAND
	select PM_OPP
	help
	help
	  This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).
	  This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).
	  It reads PPMU counters of memory controllers and adjusts the
	  It reads PPMU counters of memory controllers and adjusts the
+115 −10
Original line number Original line 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
 * @devfreq:	the devfreq struct
 * @skip:	skip calling device_unregister().
 * @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);
	mutex_lock(&devfreq_list_lock);
	if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
	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)
	if (devfreq->profile->exit)
		devfreq->profile->exit(devfreq->dev.parent);
		devfreq->profile->exit(devfreq->dev.parent);


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

	mutex_destroy(&devfreq->lock);
	mutex_destroy(&devfreq->lock);
	kfree(devfreq);
	kfree(devfreq);
}
}
@@ -426,14 +421,12 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
 * @dev:	the devfreq device
 * @dev:	the devfreq device
 *
 *
 * This calls _remove_devfreq() if _remove_devfreq() is not called.
 * 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)
static void devfreq_dev_release(struct device *dev)
{
{
	struct devfreq *devfreq = to_devfreq(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)
	if (!devfreq)
		return -EINVAL;
		return -EINVAL;


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


	return 0;
	return 0;
}
}
EXPORT_SYMBOL(devfreq_remove_device);
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_suspend_device() - Suspend devfreq of a device.
 * @devfreq: the devfreq instance to be suspended
 * @devfreq: the devfreq instance to be suspended
@@ -1112,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
	return ret;
	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_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_DESCRIPTION("devfreq class support");
MODULE_DESCRIPTION("devfreq class support");
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
+1 −1
Original line number Original line Diff line number Diff line
# Exynos DEVFREQ Drivers
# 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
obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ)	+= exynos_ppmu.o exynos5_bus.o
+60 −159
Original line number Original line Diff line number Diff line
@@ -25,13 +25,9 @@
#include <linux/regulator/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/module.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 <mach/map.h>


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


#define MAX_SAFEVOLT	1200000 /* 1.2V */
#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% */
/* Assume that the bus is saturated if the utilization is 40% */
#define BUS_SATURATION_RATIO	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 {
enum busclk_level_idx {
	LV_0 = 0,
	LV_0 = 0,
	LV_1,
	LV_1,
@@ -68,6 +48,13 @@ enum busclk_level_idx {
	LV_4,
	LV_4,
	_LV_END
	_LV_END
};
};

enum exynos_ppmu_idx {
	PPMU_DMC0,
	PPMU_DMC1,
	PPMU_END,
};

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


	struct notifier_block pm_notifier;
	struct notifier_block pm_notifier;
	struct mutex lock;
	struct mutex lock;
@@ -101,12 +88,6 @@ struct busfreq_data {
	unsigned int top_divtable[_LV_END];
	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 */
/* 4210 controls clock of mif and voltage of int */
static struct bus_opp_table exynos4210_busclk_table[] = {
static struct bus_opp_table exynos4210_busclk_table[] = {
	{LV_0, 400000, 1150000},
	{LV_0, 400000, 1150000},
@@ -524,57 +505,6 @@ static int exynos4x12_set_busclk(struct busfreq_data *data,
	return 0;
	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)
static int exynos4x12_get_intspec(unsigned long mifclk)
{
{
	int i = 0;
	int i = 0;
@@ -698,84 +628,35 @@ out:
	return err;
	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,
static int exynos4_bus_get_dev_status(struct device *dev,
				      struct devfreq_dev_status *stat)
				      struct devfreq_dev_status *stat)
{
{
	struct busfreq_data *data = dev_get_drvdata(dev);
	struct busfreq_data *data = dev_get_drvdata(dev);
	int busier_dmc;
	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
	int cycles_x2 = 2; /* 2 x cycles */
	int busier;
	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;


	if (busier_dmc)
	exynos_read_ppmu(ppmu_data);
		addr = S5P_VA_DMC1;
	busier = exynos_get_busier_ppmu(ppmu_data);
	else
	stat->current_frequency = data->curr_oppinfo.rate;
		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;
	}


	/* Number of cycles spent on memory access */
	/* 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->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 the counters have overflown, retry */
	if (data->dmc[busier_dmc].ccnt_overflow ||
	if (ppmu_data->ppmu[busier].ccnt_overflow ||
	    data->dmc[busier_dmc].count_overflow[0])
	    ppmu_data->ppmu[busier].count_overflow[0])
		return -EAGAIN;
		return -EAGAIN;


	return 0;
	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 = {
static struct devfreq_dev_profile exynos4_devfreq_profile = {
	.initial_freq	= 400000,
	.initial_freq	= 400000,
	.polling_ms	= 50,
	.polling_ms	= 50,
	.target		= exynos4_bus_target,
	.target		= exynos4_bus_target,
	.get_dev_status	= exynos4_bus_get_dev_status,
	.get_dev_status	= exynos4_bus_get_dev_status,
	.exit		= exynos4_bus_exit,
};
};


static int exynos4210_init_tables(struct busfreq_data *data)
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;
		data->top_divtable[i] = tmp;
	}
	}


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


	pr_debug("ASV Group of Exynos4 is %d\n", tmp);
	pr_debug("ASV Group of Exynos4 is %d\n", tmp);
	/* Use merged grouping for voltage */
	/* Use merged grouping for voltage */
@@ -922,11 +803,7 @@ static int exynos4x12_init_tables(struct busfreq_data *data)
		data->dmc_divtable[i] = tmp;
		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 */
	tmp = 0; /* Max voltages for the reliability of the unknown */
#endif


	if (tmp > 8)
	if (tmp > 8)
		tmp = 0;
		tmp = 0;
@@ -1020,6 +897,7 @@ unlock:
static int exynos4_busfreq_probe(struct platform_device *pdev)
static int exynos4_busfreq_probe(struct platform_device *pdev)
{
{
	struct busfreq_data *data;
	struct busfreq_data *data;
	struct busfreq_ppmu_data *ppmu_data;
	struct dev_pm_opp *opp;
	struct dev_pm_opp *opp;
	struct device *dev = &pdev->dev;
	struct device *dev = &pdev->dev;
	int err = 0;
	int err = 0;
@@ -1030,9 +908,19 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
		return -ENOMEM;
		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->type = pdev->id_entry->driver_data;
	data->dmc[0].hw_base = S5P_VA_DMC0;
	ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0;
	data->dmc[1].hw_base = S5P_VA_DMC1;
	ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1;
	data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event;
	data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event;
	data->dev = dev;
	data->dev = dev;
	mutex_init(&data->lock);
	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);
		dev_err(dev, "Cannot determine the device id %d\n", data->type);
		err = -EINVAL;
		err = -EINVAL;
	}
	}
	if (err)
	if (err) {
		dev_err(dev, "Cannot initialize busfreq table %d\n",
			     data->type);
		return err;
		return err;
	}


	data->vdd_int = devm_regulator_get(dev, "vdd_int");
	data->vdd_int = devm_regulator_get(dev, "vdd_int");
	if (IS_ERR(data->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);
	platform_set_drvdata(pdev, data);


	busfreq_mon_reset(data);
	data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile,

	data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile,
					   "simple_ondemand", NULL);
					   "simple_ondemand", NULL);
	if (IS_ERR(data->devfreq))
	if (IS_ERR(data->devfreq))
		return PTR_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);
	err = register_pm_notifier(&data->pm_notifier);
	if (err) {
	if (err) {
		dev_err(dev, "Failed to setup pm notifier\n");
		dev_err(dev, "Failed to setup pm notifier\n");
		devfreq_remove_device(data->devfreq);
		return err;
		return err;
	}
	}


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


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


	return 0;
	return 0;
}
}


#ifdef CONFIG_PM_SLEEP
static int exynos4_busfreq_resume(struct device *dev)
static int exynos4_busfreq_resume(struct device *dev)
{
{
	struct busfreq_data *data = dev_get_drvdata(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;
	return 0;
}
}
#endif


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


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


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


@@ -75,49 +75,6 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
	{0, 0, 0},
	{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,
static int exynos5_int_setvolt(struct busfreq_data_int *data,
				unsigned long volt)
				unsigned long volt)
{
{
@@ -185,59 +142,33 @@ out:
	return err;
	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,
static int exynos5_int_get_dev_status(struct device *dev,
				      struct devfreq_dev_status *stat)
				      struct devfreq_dev_status *stat)
{
{
	struct platform_device *pdev = container_of(dev, struct platform_device,
	struct platform_device *pdev = container_of(dev, struct platform_device,
						    dev);
						    dev);
	struct busfreq_data_int *data = platform_get_drvdata(pdev);
	struct busfreq_data_int *data = platform_get_drvdata(pdev);
	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
	int busier_dmc;
	int busier_dmc;


	exynos5_read_ppmu(data);
	exynos_read_ppmu(ppmu_data);
	busier_dmc = exynos5_get_busier_dmc(data);
	busier_dmc = exynos_get_busier_ppmu(ppmu_data);


	stat->current_frequency = data->curr_freq;
	stat->current_frequency = data->curr_freq;


	/* Number of cycles spent on memory access */
	/* 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->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;
	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 = {
static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
	.initial_freq		= 160000,
	.initial_freq		= 160000,
	.polling_ms		= 100,
	.polling_ms		= 100,
	.target			= exynos5_busfreq_int_target,
	.target			= exynos5_busfreq_int_target,
	.get_dev_status		= exynos5_int_get_dev_status,
	.get_dev_status		= exynos5_int_get_dev_status,
	.exit			= exynos5_int_exit,
};
};


static int exynos5250_init_int_tables(struct busfreq_data_int *data)
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)
static int exynos5_busfreq_int_probe(struct platform_device *pdev)
{
{
	struct busfreq_data_int *data;
	struct busfreq_data_int *data;
	struct busfreq_ppmu_data *ppmu_data;
	struct dev_pm_opp *opp;
	struct dev_pm_opp *opp;
	struct device *dev = &pdev->dev;
	struct device *dev = &pdev->dev;
	struct device_node *np;
	struct device_node *np;
@@ -330,16 +262,26 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
		return -ENOMEM;
		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");
	np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
	if (np == NULL) {
	if (np == NULL) {
		pr_err("Unable to find PPMU node\n");
		pr_err("Unable to find PPMU node\n");
		return -ENOENT;
		return -ENOENT;
	}
	}


	for (i = PPMU_RIGHT; i < PPMU_END; i++) {
	for (i = 0; i < ppmu_data->ppmu_end; i++) {
		/* map PPMU memory region */
		/* map PPMU memory region */
		data->ppmu[i].hw_base = of_iomap(np, i);
		ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
		if (data->ppmu[i].hw_base == NULL) {
		if (ppmu_data->ppmu[i].hw_base == NULL) {
			dev_err(&pdev->dev, "failed to map memory region\n");
			dev_err(&pdev->dev, "failed to map memory region\n");
			return -ENOMEM;
			return -ENOMEM;
		}
		}
@@ -390,32 +332,29 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)


	platform_set_drvdata(pdev, data);
	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);
					   "simple_ondemand", NULL);
	if (IS_ERR(data->devfreq))
		return PTR_ERR(data->devfreq);


	if (IS_ERR(data->devfreq)) {
	err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
		err = PTR_ERR(data->devfreq);
	if (err < 0) {
		goto err_devfreq_add;
		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);
	err = register_pm_notifier(&data->pm_notifier);
	if (err) {
	if (err) {
		dev_err(dev, "Failed to setup pm notifier\n");
		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 */
	/* TODO: Add a new QOS class for int/mif bus */
	pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);
	pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);


	return 0;
	return 0;

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


static int exynos5_busfreq_int_remove(struct platform_device *pdev)
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);
	pm_qos_remove_request(&data->int_req);
	unregister_pm_notifier(&data->pm_notifier);
	unregister_pm_notifier(&data->pm_notifier);
	devfreq_remove_device(data->devfreq);


	return 0;
	return 0;
}
}


#ifdef CONFIG_PM_SLEEP
static int exynos5_busfreq_int_resume(struct device *dev)
static int exynos5_busfreq_int_resume(struct device *dev)
{
{
	struct platform_device *pdev = container_of(dev, struct platform_device,
	struct platform_device *pdev = container_of(dev, struct platform_device,
						    dev);
						    dev);
	struct busfreq_data_int *data = platform_get_drvdata(pdev);
	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;
	return 0;
}
}

static const struct dev_pm_ops exynos5_busfreq_int_pm = {
static const struct dev_pm_ops exynos5_busfreq_int_pm = {
	.resume	= exynos5_busfreq_int_resume,
	.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. */
/* platform device pointer for exynos5 devfreq device. */
static struct platform_device *exynos5_devfreq_pdev;
static struct platform_device *exynos5_devfreq_pdev;
@@ -452,7 +394,7 @@ static struct platform_driver exynos5_busfreq_int_driver = {
	.driver		= {
	.driver		= {
		.name		= "exynos5-bus-int",
		.name		= "exynos5-bus-int",
		.owner		= THIS_MODULE,
		.owner		= THIS_MODULE,
		.pm		= &exynos5_busfreq_int_pm,
		.pm		= &exynos5_busfreq_int_pm_ops,
	},
	},
};
};


Loading