summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-02-20 14:23:40 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-02-20 14:23:40 +0100
commit41ef3d1df037a429703db503e98fa948ee34b895 (patch)
tree611d15d08b45efd9c8c9ea0ae1aa8b4498b1ba7e
parentad7eec4244a8c6b53a0d38d6fd674e9d2c92897f (diff)
parent23b017a623a0bd19c24e6c1c41fe587c644e9dbc (diff)
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
-rw-r--r--Documentation/ABI/testing/sysfs-class-devfreq-event25
-rw-r--r--Documentation/devicetree/bindings/devfreq/exynos-bus.txt14
-rw-r--r--drivers/devfreq/devfreq-event.c4
-rw-r--r--drivers/devfreq/devfreq.c74
-rw-r--r--drivers/devfreq/event/exynos-ppmu.c329
-rw-r--r--drivers/devfreq/exynos-bus.c8
-rw-r--r--drivers/devfreq/governor.h2
-rw-r--r--drivers/devfreq/governor_passive.c6
-rw-r--r--drivers/devfreq/governor_userspace.c11
-rw-r--r--include/linux/devfreq.h3
10 files changed, 350 insertions, 126 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq-event b/Documentation/ABI/testing/sysfs-class-devfreq-event
new file mode 100644
index 000000000000..ceaf0f686d4a
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-devfreq-event
@@ -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.
diff --git a/Documentation/devicetree/bindings/devfreq/exynos-bus.txt b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt
index d3ec8e676b6b..d085ef90d27c 100644
--- a/Documentation/devicetree/bindings/devfreq/exynos-bus.txt
+++ b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt
@@ -123,6 +123,20 @@ Detailed correlation between sub-blocks and power line according to Exynos SoC:
|--- FSYS
|--- 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:
Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to
power line (regulator). The MIF (Memory Interface) AXI bus is used to
diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c
index 9aea2c7ecbe6..8648b32ebc89 100644
--- a/drivers/devfreq/devfreq-event.c
+++ b/drivers/devfreq/devfreq-event.c
@@ -306,7 +306,7 @@ struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev,
struct devfreq_event_desc *desc)
{
struct devfreq_event_dev *edev;
- static atomic_t event_no = ATOMIC_INIT(0);
+ static atomic_t event_no = ATOMIC_INIT(-1);
int ret;
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.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);
if (ret < 0) {
put_device(&edev->dev);
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 253525ea17af..551a271353d2 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -128,7 +128,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
* @devfreq: the devfreq instance
* @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;
unsigned long cur_time;
@@ -164,6 +164,7 @@ out:
devfreq->last_stat_updated = cur_time;
return ret;
}
+EXPORT_SYMBOL(devfreq_update_status);
/**
* 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: the devfreq struct
+ * devfreq_dev_release() - Callback for struct device to release the device.
+ * @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);
if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
mutex_unlock(&devfreq_list_lock);
@@ -498,19 +503,6 @@ static void _remove_devfreq(struct devfreq *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
* @dev: the device to add devfreq feature.
* @profile: device-specific profile to run devfreq.
@@ -525,6 +517,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
{
struct devfreq *devfreq;
struct devfreq_governor *governor;
+ static atomic_t devfreq_no = ATOMIC_INIT(-1);
int err = 0;
if (!dev || !profile || !governor_name) {
@@ -536,15 +529,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq = find_device_devfreq(dev);
mutex_unlock(&devfreq_list_lock);
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;
goto err_out;
}
devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
if (!devfreq) {
- dev_err(dev, "%s: Unable to create devfreq for the device\n",
- __func__);
err = -ENOMEM;
goto err_out;
}
@@ -567,18 +559,21 @@ struct devfreq *devfreq_add_device(struct device *dev,
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);
if (err) {
mutex_unlock(&devfreq->lock);
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,
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,
GFP_KERNEL);
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) {
ret = 0;
goto out;
+ } else if (df->governor->immutable || governor->immutable) {
+ ret = -EINVAL;
+ goto out;
}
if (df->governor) {
@@ -966,13 +964,33 @@ static ssize_t available_governors_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
- struct devfreq_governor *tmp_governor;
+ struct devfreq *df = to_devfreq(d);
ssize_t count = 0;
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);
/* 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 &&
!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);
}
diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c
index 107eb91a9415..9b7350935b73 100644
--- a/drivers/devfreq/event/exynos-ppmu.c
+++ b/drivers/devfreq/event/exynos-ppmu.c
@@ -17,13 +17,13 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/devfreq-event.h>
#include "exynos-ppmu.h"
struct exynos_ppmu_data {
- void __iomem *base;
struct clk *clk;
};
@@ -33,6 +33,7 @@ struct exynos_ppmu {
unsigned int num_events;
struct device *dev;
+ struct regmap *regmap;
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)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
+ int ret;
u32 pmnc;
/* Disable all counters */
- __raw_writel(PPMU_CCNT_MASK |
- PPMU_PMCNT0_MASK |
- PPMU_PMCNT1_MASK |
- PPMU_PMCNT2_MASK |
- PPMU_PMCNT3_MASK,
- info->ppmu.base + PPMU_CNTENC);
+ ret = regmap_write(info->regmap, PPMU_CNTENC,
+ PPMU_CCNT_MASK |
+ PPMU_PMCNT0_MASK |
+ PPMU_PMCNT1_MASK |
+ PPMU_PMCNT2_MASK |
+ PPMU_PMCNT3_MASK);
+ if (ret < 0)
+ return ret;
/* 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;
- __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
+ ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
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);
int id = exynos_ppmu_find_ppmu_id(edev);
+ int ret;
u32 pmnc, cntens;
if (id < 0)
return id;
/* 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));
- __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 */
- __raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT,
- info->ppmu.base + PPMU_BEVTxSEL(id));
+ ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
+ PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT);
+ if (ret < 0)
+ return ret;
/* 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
| PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_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;
}
@@ -161,40 +183,64 @@ static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(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)
return -EINVAL;
/* 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;
- __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
+ ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
/* 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 */
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
- edata->load_count
- = __raw_readl(info->ppmu.base + PPMU_PMNCT(id));
+ ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
+ if (ret < 0)
+ return ret;
+ edata->load_count = load_count;
break;
case PPMU_PMNCNT3:
- edata->load_count =
- ((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8)
- | __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW));
+ ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
+ if (ret < 0)
+ 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;
default:
return -EINVAL;
}
/* 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));
- __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,
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)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
+ int ret;
u32 pmnc, clear;
/* Disable all counters */
clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
+ ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
+ 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;
- __raw_writel(clear, info->ppmu.base + PPMU_V2_FLAG);
- __raw_writel(clear, info->ppmu.base + PPMU_V2_INTENC);
- __raw_writel(clear, info->ppmu.base + PPMU_V2_CNTENC);
- __raw_writel(clear, info->ppmu.base + PPMU_V2_CNT_RESET);
-
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG0);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG1);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG2);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_RESULT);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CNT_AUTO);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV0_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV1_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV2_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV3_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_V);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_A);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_V);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_A);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_INTERRUPT_RESET);
+ 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 */
- 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;
- __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;
}
@@ -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)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
+ unsigned int pmnc, cntens;
int id = exynos_ppmu_find_ppmu_id(edev);
- u32 pmnc, cntens;
+ int ret;
/* 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));
- __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 */
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
- __raw_writel(PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT,
- info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id));
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
+ PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT);
+ if (ret < 0)
+ return ret;
break;
case PPMU_PMNCNT3:
- __raw_writel(PPMU_V2_EVT3_RW_DATA_CNT,
- info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id));
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
+ PPMU_V2_EVT3_RW_DATA_CNT);
+ if (ret < 0)
+ return ret;
break;
}
/* 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
| PPMU_PMNC_COUNTER_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_CC_RESET_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;
}
@@ -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);
int id = exynos_ppmu_find_ppmu_id(edev);
- u32 pmnc, cntenc;
- u32 pmcnt_high, pmcnt_low;
- u64 load_count = 0;
+ int ret;
+ unsigned int pmnc, cntenc;
+ unsigned int pmcnt_high, pmcnt_low;
+ unsigned int total_count, count;
+ unsigned long load_count = 0;
/* 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;
- __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 */
- 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) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
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;
case PPMU_PMNCNT3:
- pmcnt_high = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_HIGH);
- pmcnt_low = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_LOW);
- load_count = ((u64)((pmcnt_high & 0xff)) << 32)
- + (u64)pmcnt_low;
+ ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
+ &pmcnt_high);
+ if (ret < 0)
+ 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;
}
edata->load_count = load_count;
/* 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));
- __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,
edata->load_count, edata->total_count);
@@ -411,10 +554,19 @@ static int of_get_devfreq_events(struct device_node *np,
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_node *np = dev->of_node;
+ struct resource *res;
+ void __iomem *base;
int ret = 0;
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 */
- info->ppmu.base = of_iomap(np, 0);
- if (IS_ERR_OR_NULL(info->ppmu.base)) {
- dev_err(dev, "failed to map memory region\n");
- return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ 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");
@@ -438,15 +597,10 @@ static int exynos_ppmu_parse_dt(struct exynos_ppmu *info)
ret = of_get_devfreq_events(np, info);
if (ret < 0) {
dev_err(dev, "failed to parse exynos ppmu dt node\n");
- goto err;
+ return ret;
}
return 0;
-
-err:
- iounmap(info->ppmu.base);
-
- return ret;
}
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;
/* Parse dt data to get resource */
- ret = exynos_ppmu_parse_dt(info);
+ ret = exynos_ppmu_parse_dt(pdev, info);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to parse devicetree for resource\n");
@@ -476,8 +630,7 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
if (!info->edev) {
dev_err(&pdev->dev,
"failed to allocate memory devfreq-event devices\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
edev = info->edev;
platform_set_drvdata(pdev, info);
@@ -488,17 +641,16 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
ret = PTR_ERR(edev[i]);
dev_err(&pdev->dev,
"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);
return 0;
-err:
- iounmap(info->ppmu.base);
-
- return ret;
}
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);
clk_disable_unprepare(info->ppmu.clk);
- iounmap(info->ppmu.base);
return 0;
}
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
index c6d850cddd98..49f68929e024 100644
--- a/drivers/devfreq/exynos-bus.c
+++ b/drivers/devfreq/exynos-bus.c
@@ -146,8 +146,8 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
}
bus->curr_freq = new_freq;
- dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n",
- old_freq/1000, new_freq/1000);
+ dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
+ old_freq, new_freq, clk_get_rate(bus->clk));
out:
mutex_unlock(&bus->lock);
@@ -239,8 +239,8 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
*freq = new_freq;
bus->curr_freq = new_freq;
- dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n",
- old_freq/1000, new_freq/1000);
+ dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
+ old_freq, new_freq, clk_get_rate(bus->clk));
out:
mutex_unlock(&bus->lock);
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
index fad7d6321978..71576b8bdfef 100644
--- a/drivers/devfreq/governor.h
+++ b/drivers/devfreq/governor.h
@@ -38,4 +38,6 @@ extern void devfreq_interval_update(struct devfreq *devfreq,
extern int devfreq_add_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 */
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index bd452236dba4..673ad8cc9a1d 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -112,6 +112,11 @@ static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
if (ret < 0)
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;
out:
@@ -179,6 +184,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_passive = {
.name = "passive",
+ .immutable = 1,
.get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler,
};
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c
index 35de6e83c1fe..176976068bcd 100644
--- a/drivers/devfreq/governor_userspace.c
+++ b/drivers/devfreq/governor_userspace.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/devfreq/governor_simpleondemand.c
+ * linux/drivers/devfreq/governor_userspace.c
*
* Copyright (C) 2011 Samsung Electronics
* 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;
int err = 0;
-
mutex_lock(&devfreq->lock);
data = devfreq->data;
@@ -112,7 +111,13 @@ out:
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);
devfreq->data = NULL;
}
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 2de4e2eea180..e0acb0e5243b 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -104,6 +104,8 @@ struct devfreq_dev_profile {
* struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors
* @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.
* Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the
@@ -121,6 +123,7 @@ struct devfreq_governor {
struct list_head node;
const char name[DEVFREQ_NAME_LEN];
+ const unsigned int immutable;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data);