diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-02-20 14:23:40 +0100 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-02-20 14:23:40 +0100 |
commit | 41ef3d1df037a429703db503e98fa948ee34b895 (patch) | |
tree | 611d15d08b45efd9c8c9ea0ae1aa8b4498b1ba7e | |
parent | ad7eec4244a8c6b53a0d38d6fd674e9d2c92897f (diff) | |
parent | 23b017a623a0bd19c24e6c1c41fe587c644e9dbc (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-event | 25 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/devfreq/exynos-bus.txt | 14 | ||||
-rw-r--r-- | drivers/devfreq/devfreq-event.c | 4 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 74 | ||||
-rw-r--r-- | drivers/devfreq/event/exynos-ppmu.c | 329 | ||||
-rw-r--r-- | drivers/devfreq/exynos-bus.c | 8 | ||||
-rw-r--r-- | drivers/devfreq/governor.h | 2 | ||||
-rw-r--r-- | drivers/devfreq/governor_passive.c | 6 | ||||
-rw-r--r-- | drivers/devfreq/governor_userspace.c | 11 | ||||
-rw-r--r-- | include/linux/devfreq.h | 3 |
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); |