1
0
Fork 0

Merge branch 'pm-devfreq'

* pm-devfreq:
  PM / devfreq: Modify the device name as devfreq(X) for sysfs
  PM / devfreq: Simplify the sysfs name of devfreq-event device
  PM / devfreq: Remove unnecessary separate _remove_devfreq()
  PM / devfreq: Fix wrong trans_stat of passive devfreq device
  PM / devfreq: Fix available_governor sysfs
  PM / devfreq: exynos-ppmu: Show the registred device for ppmu device
  PM / devfreq: Fix the wrong description for userspace governor
  PM / devfreq: Fix the checkpatch warnings
  PM / devfreq: exynos-bus: Print the real clock rate of bus
  PM / devfreq: exynos-ppmu: Use the regmap interface to handle the registers
  PM / devfreq: exynos-bus: Add the detailed correlation for Exynos5433
  PM / devfreq: Don't delete sysfs group twice
zero-colors
Rafael J. Wysocki 2017-02-20 14:23:40 +01:00
commit 41ef3d1df0
10 changed files with 349 additions and 125 deletions

View File

@ -0,0 +1,25 @@
What: /sys/class/devfreq-event/event(x)/
Date: January 2017
Contact: Chanwoo Choi <cw00.choi@samsung.com>
Description:
Provide a place in sysfs for the devfreq-event objects.
This allows accessing various devfreq-event specific variables.
The name of devfreq-event object denoted as 'event(x)' which
includes the unique number of 'x' for each devfreq-event object.
What: /sys/class/devfreq-event/event(x)/name
Date: January 2017
Contact: Chanwoo Choi <cw00.choi@samsung.com>
Description:
The /sys/class/devfreq-event/event(x)/name attribute contains
the name of the devfreq-event object. This attribute is
read-only.
What: /sys/class/devfreq-event/event(x)/enable_count
Date: January 2017
Contact: Chanwoo Choi <cw00.choi@samsung.com>
Description:
The /sys/class/devfreq-event/event(x)/enable_count attribute
contains the reference count to enable the devfreq-event
object. If the device is enabled, the value of attribute is
greater than zero.

View File

@ -123,6 +123,20 @@ Detailed correlation between sub-blocks and power line according to Exynos SoC:
|--- FSYS |--- FSYS
|--- FSYS2 |--- FSYS2
- In case of Exynos5433, there is VDD_INT power line as following:
VDD_INT |--- G2D (parent device)
|--- MSCL
|--- GSCL
|--- JPEG
|--- MFC
|--- HEVC
|--- BUS0
|--- BUS1
|--- BUS2
|--- PERIS (Fixed clock rate)
|--- PERIC (Fixed clock rate)
|--- FSYS (Fixed clock rate)
Example1: Example1:
Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to
power line (regulator). The MIF (Memory Interface) AXI bus is used to power line (regulator). The MIF (Memory Interface) AXI bus is used to

View File

@ -306,7 +306,7 @@ struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev,
struct devfreq_event_desc *desc) struct devfreq_event_desc *desc)
{ {
struct devfreq_event_dev *edev; struct devfreq_event_dev *edev;
static atomic_t event_no = ATOMIC_INIT(0); static atomic_t event_no = ATOMIC_INIT(-1);
int ret; int ret;
if (!dev || !desc) if (!dev || !desc)
@ -329,7 +329,7 @@ struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev,
edev->dev.class = devfreq_event_class; edev->dev.class = devfreq_event_class;
edev->dev.release = devfreq_event_release_edev; edev->dev.release = devfreq_event_release_edev;
dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1); dev_set_name(&edev->dev, "event%d", atomic_inc_return(&event_no));
ret = device_register(&edev->dev); ret = device_register(&edev->dev);
if (ret < 0) { if (ret < 0) {
put_device(&edev->dev); put_device(&edev->dev);

View File

@ -128,7 +128,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
* @devfreq: the devfreq instance * @devfreq: the devfreq instance
* @freq: the update target frequency * @freq: the update target frequency
*/ */
static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
{ {
int lev, prev_lev, ret = 0; int lev, prev_lev, ret = 0;
unsigned long cur_time; unsigned long cur_time;
@ -164,6 +164,7 @@ out:
devfreq->last_stat_updated = cur_time; devfreq->last_stat_updated = cur_time;
return ret; return ret;
} }
EXPORT_SYMBOL(devfreq_update_status);
/** /**
* find_devfreq_governor() - find devfreq governor from name * find_devfreq_governor() - find devfreq governor from name
@ -472,11 +473,15 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
} }
/** /**
* _remove_devfreq() - Remove devfreq from the list and release its resources. * devfreq_dev_release() - Callback for struct device to release the device.
* @devfreq: the devfreq struct * @dev: the devfreq device
*
* Remove devfreq from the list and release its resources.
*/ */
static void _remove_devfreq(struct devfreq *devfreq) static void devfreq_dev_release(struct device *dev)
{ {
struct devfreq *devfreq = to_devfreq(dev);
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))) {
mutex_unlock(&devfreq_list_lock); mutex_unlock(&devfreq_list_lock);
@ -497,19 +502,6 @@ static void _remove_devfreq(struct devfreq *devfreq)
kfree(devfreq); kfree(devfreq);
} }
/**
* devfreq_dev_release() - Callback for struct device to release the device.
* @dev: the devfreq device
*
* This calls _remove_devfreq() if _remove_devfreq() is not called.
*/
static void devfreq_dev_release(struct device *dev)
{
struct devfreq *devfreq = to_devfreq(dev);
_remove_devfreq(devfreq);
}
/** /**
* devfreq_add_device() - Add devfreq feature to the device * devfreq_add_device() - Add devfreq feature to the device
* @dev: the device to add devfreq feature. * @dev: the device to add devfreq feature.
@ -525,6 +517,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
{ {
struct devfreq *devfreq; struct devfreq *devfreq;
struct devfreq_governor *governor; struct devfreq_governor *governor;
static atomic_t devfreq_no = ATOMIC_INIT(-1);
int err = 0; int err = 0;
if (!dev || !profile || !governor_name) { if (!dev || !profile || !governor_name) {
@ -536,15 +529,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq = find_device_devfreq(dev); devfreq = find_device_devfreq(dev);
mutex_unlock(&devfreq_list_lock); mutex_unlock(&devfreq_list_lock);
if (!IS_ERR(devfreq)) { if (!IS_ERR(devfreq)) {
dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__); dev_err(dev, "%s: Unable to create devfreq for the device.\n",
__func__);
err = -EINVAL; err = -EINVAL;
goto err_out; goto err_out;
} }
devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL); devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
if (!devfreq) { if (!devfreq) {
dev_err(dev, "%s: Unable to create devfreq for the device\n",
__func__);
err = -ENOMEM; err = -ENOMEM;
goto err_out; goto err_out;
} }
@ -567,18 +559,21 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
} }
dev_set_name(&devfreq->dev, "%s", dev_name(dev)); dev_set_name(&devfreq->dev, "devfreq%d",
atomic_inc_return(&devfreq_no));
err = device_register(&devfreq->dev); err = device_register(&devfreq->dev);
if (err) { if (err) {
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
goto err_out; goto err_out;
} }
devfreq->trans_table = devm_kzalloc(&devfreq->dev, sizeof(unsigned int) * devfreq->trans_table = devm_kzalloc(&devfreq->dev,
sizeof(unsigned int) *
devfreq->profile->max_state * devfreq->profile->max_state *
devfreq->profile->max_state, devfreq->profile->max_state,
GFP_KERNEL); GFP_KERNEL);
devfreq->time_in_state = devm_kzalloc(&devfreq->dev, sizeof(unsigned long) * devfreq->time_in_state = devm_kzalloc(&devfreq->dev,
sizeof(unsigned long) *
devfreq->profile->max_state, devfreq->profile->max_state,
GFP_KERNEL); GFP_KERNEL);
devfreq->last_stat_updated = jiffies; devfreq->last_stat_updated = jiffies;
@ -937,6 +932,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
if (df->governor == governor) { if (df->governor == governor) {
ret = 0; ret = 0;
goto out; goto out;
} else if (df->governor->immutable || governor->immutable) {
ret = -EINVAL;
goto out;
} }
if (df->governor) { if (df->governor) {
@ -966,13 +964,33 @@ static ssize_t available_governors_show(struct device *d,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct devfreq_governor *tmp_governor; struct devfreq *df = to_devfreq(d);
ssize_t count = 0; ssize_t count = 0;
mutex_lock(&devfreq_list_lock); mutex_lock(&devfreq_list_lock);
list_for_each_entry(tmp_governor, &devfreq_governor_list, node)
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), /*
"%s ", tmp_governor->name); * The devfreq with immutable governor (e.g., passive) shows
* only own governor.
*/
if (df->governor->immutable) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
"%s ", df->governor_name);
/*
* The devfreq device shows the registered governor except for
* immutable governors such as passive governor .
*/
} else {
struct devfreq_governor *governor;
list_for_each_entry(governor, &devfreq_governor_list, node) {
if (governor->immutable)
continue;
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%s ", governor->name);
}
}
mutex_unlock(&devfreq_list_lock); mutex_unlock(&devfreq_list_lock);
/* Truncate the trailing space */ /* Truncate the trailing space */
@ -993,7 +1011,7 @@ static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr,
if (devfreq->profile->get_cur_freq && if (devfreq->profile->get_cur_freq &&
!devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
return sprintf(buf, "%lu\n", freq); return sprintf(buf, "%lu\n", freq);
return sprintf(buf, "%lu\n", devfreq->previous_freq); return sprintf(buf, "%lu\n", devfreq->previous_freq);
} }

View File

@ -17,13 +17,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/devfreq-event.h> #include <linux/devfreq-event.h>
#include "exynos-ppmu.h" #include "exynos-ppmu.h"
struct exynos_ppmu_data { struct exynos_ppmu_data {
void __iomem *base;
struct clk *clk; struct clk *clk;
}; };
@ -33,6 +33,7 @@ struct exynos_ppmu {
unsigned int num_events; unsigned int num_events;
struct device *dev; struct device *dev;
struct regmap *regmap;
struct exynos_ppmu_data ppmu; struct exynos_ppmu_data ppmu;
}; };
@ -107,20 +108,28 @@ static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
static int exynos_ppmu_disable(struct devfreq_event_dev *edev) static int exynos_ppmu_disable(struct devfreq_event_dev *edev)
{ {
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int ret;
u32 pmnc; u32 pmnc;
/* Disable all counters */ /* Disable all counters */
__raw_writel(PPMU_CCNT_MASK | ret = regmap_write(info->regmap, PPMU_CNTENC,
PPMU_PMCNT0_MASK | PPMU_CCNT_MASK |
PPMU_PMCNT1_MASK | PPMU_PMCNT0_MASK |
PPMU_PMCNT2_MASK | PPMU_PMCNT1_MASK |
PPMU_PMCNT3_MASK, PPMU_PMCNT2_MASK |
info->ppmu.base + PPMU_CNTENC); PPMU_PMCNT3_MASK);
if (ret < 0)
return ret;
/* Disable PPMU */ /* Disable PPMU */
pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK; pmnc &= ~PPMU_PMNC_ENABLE_MASK;
__raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
if (ret < 0)
return ret;
return 0; return 0;
} }
@ -129,29 +138,42 @@ static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
{ {
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev); int id = exynos_ppmu_find_ppmu_id(edev);
int ret;
u32 pmnc, cntens; u32 pmnc, cntens;
if (id < 0) if (id < 0)
return id; return id;
/* Enable specific counter */ /* Enable specific counter */
cntens = __raw_readl(info->ppmu.base + PPMU_CNTENS); ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
if (ret < 0)
return ret;
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
__raw_writel(cntens, info->ppmu.base + PPMU_CNTENS); ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
if (ret < 0)
return ret;
/* Set the event of Read/Write data count */ /* Set the event of Read/Write data count */
__raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT, ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
info->ppmu.base + PPMU_BEVTxSEL(id)); PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT);
if (ret < 0)
return ret;
/* Reset cycle counter/performance counter and enable PPMU */ /* Reset cycle counter/performance counter and enable PPMU */
pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~(PPMU_PMNC_ENABLE_MASK pmnc &= ~(PPMU_PMNC_ENABLE_MASK
| PPMU_PMNC_COUNTER_RESET_MASK | PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK); | PPMU_PMNC_CC_RESET_MASK);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT); pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT); pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT); pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
__raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
if (ret < 0)
return ret;
return 0; return 0;
} }
@ -161,40 +183,64 @@ static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
{ {
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev); int id = exynos_ppmu_find_ppmu_id(edev);
u32 pmnc, cntenc; unsigned int total_count, load_count;
unsigned int pmcnt3_high, pmcnt3_low;
unsigned int pmnc, cntenc;
int ret;
if (id < 0) if (id < 0)
return -EINVAL; return -EINVAL;
/* Disable PPMU */ /* Disable PPMU */
pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK; pmnc &= ~PPMU_PMNC_ENABLE_MASK;
__raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
if (ret < 0)
return ret;
/* Read cycle count */ /* Read cycle count */
edata->total_count = __raw_readl(info->ppmu.base + PPMU_CCNT); ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
if (ret < 0)
return ret;
edata->total_count = total_count;
/* Read performance count */ /* Read performance count */
switch (id) { switch (id) {
case PPMU_PMNCNT0: case PPMU_PMNCNT0:
case PPMU_PMNCNT1: case PPMU_PMNCNT1:
case PPMU_PMNCNT2: case PPMU_PMNCNT2:
edata->load_count ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
= __raw_readl(info->ppmu.base + PPMU_PMNCT(id)); if (ret < 0)
return ret;
edata->load_count = load_count;
break; break;
case PPMU_PMNCNT3: case PPMU_PMNCNT3:
edata->load_count = ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8) if (ret < 0)
| __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW)); return ret;
ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
if (ret < 0)
return ret;
edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
/* Disable specific counter */ /* Disable specific counter */
cntenc = __raw_readl(info->ppmu.base + PPMU_CNTENC); ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
if (ret < 0)
return ret;
cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
__raw_writel(cntenc, info->ppmu.base + PPMU_CNTENC); ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
if (ret < 0)
return ret;
dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
edata->load_count, edata->total_count); edata->load_count, edata->total_count);
@ -214,36 +260,93 @@ static const struct devfreq_event_ops exynos_ppmu_ops = {
static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev) static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
{ {
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int ret;
u32 pmnc, clear; u32 pmnc, clear;
/* Disable all counters */ /* Disable all counters */
clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK); | PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
if (ret < 0)
return ret;
__raw_writel(clear, info->ppmu.base + PPMU_V2_FLAG); ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
__raw_writel(clear, info->ppmu.base + PPMU_V2_INTENC); if (ret < 0)
__raw_writel(clear, info->ppmu.base + PPMU_V2_CNTENC); return ret;
__raw_writel(clear, info->ppmu.base + PPMU_V2_CNT_RESET);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG0); ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG1); if (ret < 0)
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG2); return ret;
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_RESULT);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CNT_AUTO); ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV0_TYPE); if (ret < 0)
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV1_TYPE); return ret;
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV2_TYPE);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV3_TYPE); ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_V); if (ret < 0)
__raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_A); return ret;
__raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_V);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_A); ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
__raw_writel(0x0, info->ppmu.base + PPMU_V2_INTERRUPT_RESET); if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
if (ret < 0)
return ret;
/* Disable PPMU */ /* Disable PPMU */
pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC); ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK; pmnc &= ~PPMU_PMNC_ENABLE_MASK;
__raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC); ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
if (ret < 0)
return ret;
return 0; return 0;
} }
@ -251,30 +354,43 @@ static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev) static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
{ {
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
unsigned int pmnc, cntens;
int id = exynos_ppmu_find_ppmu_id(edev); int id = exynos_ppmu_find_ppmu_id(edev);
u32 pmnc, cntens; int ret;
/* Enable all counters */ /* Enable all counters */
cntens = __raw_readl(info->ppmu.base + PPMU_V2_CNTENS); ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
if (ret < 0)
return ret;
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
__raw_writel(cntens, info->ppmu.base + PPMU_V2_CNTENS); ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
if (ret < 0)
return ret;
/* Set the event of Read/Write data count */ /* Set the event of Read/Write data count */
switch (id) { switch (id) {
case PPMU_PMNCNT0: case PPMU_PMNCNT0:
case PPMU_PMNCNT1: case PPMU_PMNCNT1:
case PPMU_PMNCNT2: case PPMU_PMNCNT2:
__raw_writel(PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT, ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id)); PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT);
if (ret < 0)
return ret;
break; break;
case PPMU_PMNCNT3: case PPMU_PMNCNT3:
__raw_writel(PPMU_V2_EVT3_RW_DATA_CNT, ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id)); PPMU_V2_EVT3_RW_DATA_CNT);
if (ret < 0)
return ret;
break; break;
} }
/* Reset cycle counter/performance counter and enable PPMU */ /* Reset cycle counter/performance counter and enable PPMU */
pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC); ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~(PPMU_PMNC_ENABLE_MASK pmnc &= ~(PPMU_PMNC_ENABLE_MASK
| PPMU_PMNC_COUNTER_RESET_MASK | PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK | PPMU_PMNC_CC_RESET_MASK
@ -284,7 +400,10 @@ static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT); pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT); pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT); pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
__raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC);
ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
if (ret < 0)
return ret;
return 0; return 0;
} }
@ -294,37 +413,61 @@ static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,
{ {
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev); int id = exynos_ppmu_find_ppmu_id(edev);
u32 pmnc, cntenc; int ret;
u32 pmcnt_high, pmcnt_low; unsigned int pmnc, cntenc;
u64 load_count = 0; unsigned int pmcnt_high, pmcnt_low;
unsigned int total_count, count;
unsigned long load_count = 0;
/* Disable PPMU */ /* Disable PPMU */
pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC); ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK; pmnc &= ~PPMU_PMNC_ENABLE_MASK;
__raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC); ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
if (ret < 0)
return ret;
/* Read cycle count and performance count */ /* Read cycle count and performance count */
edata->total_count = __raw_readl(info->ppmu.base + PPMU_V2_CCNT); ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
if (ret < 0)
return ret;
edata->total_count = total_count;
switch (id) { switch (id) {
case PPMU_PMNCNT0: case PPMU_PMNCNT0:
case PPMU_PMNCNT1: case PPMU_PMNCNT1:
case PPMU_PMNCNT2: case PPMU_PMNCNT2:
load_count = __raw_readl(info->ppmu.base + PPMU_V2_PMNCT(id)); ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
if (ret < 0)
return ret;
load_count = count;
break; break;
case PPMU_PMNCNT3: case PPMU_PMNCNT3:
pmcnt_high = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_HIGH); ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
pmcnt_low = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_LOW); &pmcnt_high);
load_count = ((u64)((pmcnt_high & 0xff)) << 32) if (ret < 0)
+ (u64)pmcnt_low; return ret;
ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
if (ret < 0)
return ret;
load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
break; break;
} }
edata->load_count = load_count; edata->load_count = load_count;
/* Disable all counters */ /* Disable all counters */
cntenc = __raw_readl(info->ppmu.base + PPMU_V2_CNTENC); ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
if (ret < 0)
return 0;
cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
__raw_writel(cntenc, info->ppmu.base + PPMU_V2_CNTENC); ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
if (ret < 0)
return ret;
dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name, dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
edata->load_count, edata->total_count); edata->load_count, edata->total_count);
@ -411,10 +554,19 @@ static int of_get_devfreq_events(struct device_node *np,
return 0; return 0;
} }
static int exynos_ppmu_parse_dt(struct exynos_ppmu *info) static struct regmap_config exynos_ppmu_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int exynos_ppmu_parse_dt(struct platform_device *pdev,
struct exynos_ppmu *info)
{ {
struct device *dev = info->dev; struct device *dev = info->dev;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct resource *res;
void __iomem *base;
int ret = 0; int ret = 0;
if (!np) { if (!np) {
@ -423,10 +575,17 @@ static int exynos_ppmu_parse_dt(struct exynos_ppmu *info)
} }
/* Maps the memory mapped IO to control PPMU register */ /* Maps the memory mapped IO to control PPMU register */
info->ppmu.base = of_iomap(np, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (IS_ERR_OR_NULL(info->ppmu.base)) { base = devm_ioremap_resource(dev, res);
dev_err(dev, "failed to map memory region\n"); if (IS_ERR(base))
return -ENOMEM; return PTR_ERR(base);
exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
info->regmap = devm_regmap_init_mmio(dev, base,
&exynos_ppmu_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(dev, "failed to initialize regmap\n");
return PTR_ERR(info->regmap);
} }
info->ppmu.clk = devm_clk_get(dev, "ppmu"); info->ppmu.clk = devm_clk_get(dev, "ppmu");
@ -438,15 +597,10 @@ static int exynos_ppmu_parse_dt(struct exynos_ppmu *info)
ret = of_get_devfreq_events(np, info); ret = of_get_devfreq_events(np, info);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "failed to parse exynos ppmu dt node\n"); dev_err(dev, "failed to parse exynos ppmu dt node\n");
goto err; return ret;
} }
return 0; return 0;
err:
iounmap(info->ppmu.base);
return ret;
} }
static int exynos_ppmu_probe(struct platform_device *pdev) static int exynos_ppmu_probe(struct platform_device *pdev)
@ -463,7 +617,7 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
info->dev = &pdev->dev; info->dev = &pdev->dev;
/* Parse dt data to get resource */ /* Parse dt data to get resource */
ret = exynos_ppmu_parse_dt(info); ret = exynos_ppmu_parse_dt(pdev, info);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to parse devicetree for resource\n"); "failed to parse devicetree for resource\n");
@ -476,8 +630,7 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
if (!info->edev) { if (!info->edev) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to allocate memory devfreq-event devices\n"); "failed to allocate memory devfreq-event devices\n");
ret = -ENOMEM; return -ENOMEM;
goto err;
} }
edev = info->edev; edev = info->edev;
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
@ -488,17 +641,16 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
ret = PTR_ERR(edev[i]); ret = PTR_ERR(edev[i]);
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to add devfreq-event device\n"); "failed to add devfreq-event device\n");
goto err; return PTR_ERR(edev[i]);
} }
pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
dev_name(&pdev->dev), desc[i].name);
} }
clk_prepare_enable(info->ppmu.clk); clk_prepare_enable(info->ppmu.clk);
return 0; return 0;
err:
iounmap(info->ppmu.base);
return ret;
} }
static int exynos_ppmu_remove(struct platform_device *pdev) static int exynos_ppmu_remove(struct platform_device *pdev)
@ -506,7 +658,6 @@ static int exynos_ppmu_remove(struct platform_device *pdev)
struct exynos_ppmu *info = platform_get_drvdata(pdev); struct exynos_ppmu *info = platform_get_drvdata(pdev);
clk_disable_unprepare(info->ppmu.clk); clk_disable_unprepare(info->ppmu.clk);
iounmap(info->ppmu.base);
return 0; return 0;
} }

View File

@ -146,8 +146,8 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
} }
bus->curr_freq = new_freq; bus->curr_freq = new_freq;
dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
old_freq/1000, new_freq/1000); old_freq, new_freq, clk_get_rate(bus->clk));
out: out:
mutex_unlock(&bus->lock); mutex_unlock(&bus->lock);
@ -239,8 +239,8 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
*freq = new_freq; *freq = new_freq;
bus->curr_freq = new_freq; bus->curr_freq = new_freq;
dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
old_freq/1000, new_freq/1000); old_freq, new_freq, clk_get_rate(bus->clk));
out: out:
mutex_unlock(&bus->lock); mutex_unlock(&bus->lock);

View File

@ -38,4 +38,6 @@ extern void devfreq_interval_update(struct devfreq *devfreq,
extern int devfreq_add_governor(struct devfreq_governor *governor); extern int devfreq_add_governor(struct devfreq_governor *governor);
extern int devfreq_remove_governor(struct devfreq_governor *governor); extern int devfreq_remove_governor(struct devfreq_governor *governor);
extern int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
#endif /* _GOVERNOR_H */ #endif /* _GOVERNOR_H */

View File

@ -112,6 +112,11 @@ static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
if (ret < 0) if (ret < 0)
goto out; goto out;
if (devfreq->profile->freq_table
&& (devfreq_update_status(devfreq, freq)))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = freq; devfreq->previous_freq = freq;
out: out:
@ -179,6 +184,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_passive = { static struct devfreq_governor devfreq_passive = {
.name = "passive", .name = "passive",
.immutable = 1,
.get_target_freq = devfreq_passive_get_target_freq, .get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler, .event_handler = devfreq_passive_event_handler,
}; };

View File

@ -1,5 +1,5 @@
/* /*
* linux/drivers/devfreq/governor_simpleondemand.c * linux/drivers/devfreq/governor_userspace.c
* *
* Copyright (C) 2011 Samsung Electronics * Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com> * MyungJoo Ham <myungjoo.ham@samsung.com>
@ -50,7 +50,6 @@ static ssize_t store_freq(struct device *dev, struct device_attribute *attr,
unsigned long wanted; unsigned long wanted;
int err = 0; int err = 0;
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
data = devfreq->data; data = devfreq->data;
@ -112,7 +111,13 @@ out:
static void userspace_exit(struct devfreq *devfreq) static void userspace_exit(struct devfreq *devfreq)
{ {
sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group); /*
* Remove the sysfs entry, unless this is being called after
* device_del(), which should have done this already via kobject_del().
*/
if (devfreq->dev.kobj.sd)
sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
kfree(devfreq->data); kfree(devfreq->data);
devfreq->data = NULL; devfreq->data = NULL;
} }

View File

@ -104,6 +104,8 @@ struct devfreq_dev_profile {
* struct devfreq_governor - Devfreq policy governor * struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors * @node: list node - contains registered devfreq governors
* @name: Governor's name * @name: Governor's name
* @immutable: Immutable flag for governor. If the value is 1,
* this govenror is never changeable to other governor.
* @get_target_freq: Returns desired operating frequency for the device. * @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run * Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the * devfreq_dev_profile.get_dev_status() to get the
@ -121,6 +123,7 @@ struct devfreq_governor {
struct list_head node; struct list_head node;
const char name[DEVFREQ_NAME_LEN]; const char name[DEVFREQ_NAME_LEN];
const unsigned int immutable;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq); int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq, int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data); unsigned int event, void *data);