* Re: [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver
@ 2015-01-12 8:04 ` MyungJoo Ham
0 siblings, 0 replies; 6+ messages in thread
From: MyungJoo Ham @ 2015-01-12 8:04 UTC (permalink / raw)
To: 최찬우, kgene@kernel.org
Cc: 박경민, rafael.j.wysocki@intel.com,
mark.rutland@arm.com, ABHILASH KESAVAN, tomasz.figa@gmail.com,
Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz,
robh+dt@kernel.org, 대인기,
linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-samsung-soc@vger.kernel.org
>
> This patch adds exynos-ppmu devfreq-event driver to get performance data
> of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide
> useful information about the behavior of the SoC that you can use when
> analyzing system performance, and made visible and can be counted using
> logic in each IP.
>
> This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c
>
> Cc: MyungJoo Ham <myungjoo.ham@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
> drivers/devfreq/event/Kconfig | 9 +
> drivers/devfreq/event/Makefile | 1 +
> drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 409 insertions(+)
> create mode 100644 drivers/devfreq/event/exynos-ppmu.c
>
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver @ 2015-01-12 8:04 ` MyungJoo Ham 0 siblings, 0 replies; 6+ messages in thread From: MyungJoo Ham @ 2015-01-12 8:04 UTC (permalink / raw) To: 최찬우, kgene@kernel.org Cc: 박경민, rafael.j.wysocki@intel.com, mark.rutland@arm.com, ABHILASH KESAVAN, tomasz.figa@gmail.com, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz, robh+dt@kernel.org, 대인기, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset=utf-8, Size: 1040 bytes --] > > This patch adds exynos-ppmu devfreq-event driver to get performance data > of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide > useful information about the behavior of the SoC that you can use when > analyzing system performance, and made visible and can be counted using > logic in each IP. > > This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c > > Cc: MyungJoo Ham <myungjoo.ham@samsung.com> > Cc: Kyungmin Park <kyungmin.park@samsung.com> > Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> > --- > drivers/devfreq/event/Kconfig | 9 + > drivers/devfreq/event/Makefile | 1 + > drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 409 insertions(+) > create mode 100644 drivers/devfreq/event/exynos-ppmu.c > Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com> ÿôèº{.nÇ+·®+%Ëÿ±éݶ\x17¥wÿº{.nÇ+·¥{±þG«éÿ{ayº\x1dÊÚë,j\a¢f£¢·hïêÿêçz_è®\x03(éÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?¨èÚ&£ø§~á¶iOæ¬z·vØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?I¥ ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver 2015-01-12 8:04 ` MyungJoo Ham @ 2015-01-12 8:39 ` Chanwoo Choi -1 siblings, 0 replies; 6+ messages in thread From: Chanwoo Choi @ 2015-01-12 8:39 UTC (permalink / raw) To: myungjoo.ham Cc: kgene@kernel.org, 박경민, rafael.j.wysocki@intel.com, mark.rutland@arm.com, ABHILASH KESAVAN, tomasz.figa@gmail.com, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz, robh+dt@kernel.org, 대인기, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org Dear Myungjoo, On 01/12/2015 05:04 PM, MyungJoo Ham wrote: >> >> This patch adds exynos-ppmu devfreq-event driver to get performance data >> of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide >> useful information about the behavior of the SoC that you can use when >> analyzing system performance, and made visible and can be counted using >> logic in each IP. >> >> This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c >> >> Cc: MyungJoo Ham <myungjoo.ham@samsung.com> >> Cc: Kyungmin Park <kyungmin.park@samsung.com> >> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> >> --- >> drivers/devfreq/event/Kconfig | 9 + >> drivers/devfreq/event/Makefile | 1 + >> drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++++++++++++++++++++++++ >> 3 files changed, 409 insertions(+) >> create mode 100644 drivers/devfreq/event/exynos-ppmu.c >> > > Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com> > Thanks for your reviw. Best Regards, Chanwoo Choi ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver @ 2015-01-12 8:39 ` Chanwoo Choi 0 siblings, 0 replies; 6+ messages in thread From: Chanwoo Choi @ 2015-01-12 8:39 UTC (permalink / raw) To: linux-arm-kernel Dear Myungjoo, On 01/12/2015 05:04 PM, MyungJoo Ham wrote: >> >> This patch adds exynos-ppmu devfreq-event driver to get performance data >> of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide >> useful information about the behavior of the SoC that you can use when >> analyzing system performance, and made visible and can be counted using >> logic in each IP. >> >> This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c >> >> Cc: MyungJoo Ham <myungjoo.ham@samsung.com> >> Cc: Kyungmin Park <kyungmin.park@samsung.com> >> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> >> --- >> drivers/devfreq/event/Kconfig | 9 + >> drivers/devfreq/event/Makefile | 1 + >> drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++++++++++++++++++++++++ >> 3 files changed, 409 insertions(+) >> create mode 100644 drivers/devfreq/event/exynos-ppmu.c >> > > Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com> > Thanks for your reviw. Best Regards, Chanwoo Choi ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCHv7 00/10] devfreq: Add devfreq-event class to provide raw data for devfreq device
@ 2015-01-07 23:51 Chanwoo Choi
2015-01-07 23:51 ` Chanwoo Choi
0 siblings, 1 reply; 6+ messages in thread
From: Chanwoo Choi @ 2015-01-07 23:51 UTC (permalink / raw)
To: myungjoo.ham, kgene
Cc: kyungmin.park, rafael.j.wysocki, mark.rutland, a.kesavan,
tomasz.figa, k.kozlowski, b.zolnierkie, robh+dt, cw00.choi,
inki.dae, linux-pm, linux-kernel, linux-arm-kernel,
linux-samsung-soc
This patchset add new devfreq_event class to provide raw data to determine
current utilization of device which is used for devfreq governor.
The following description explains the feature of two kind of devfreq class:
- devfreq class (existing)
: devfreq consumer device use raw data from devfreq_event device for
determining proper current system state and change voltage/frequency
dynamically using various governors.
- devfreq_event class (new)
: Provide measured raw data to devfreq device for governor
---------------
Changes from v6:
- This patchset is based on v3.19-rc3.
1. devfreq-event class driver
- Fix build break if devfreq-event framework is off
- Add resource-managed function for devfreq-event device
: devm_devfreq_event_add_edev()
: devm_devfreq_event_remove_edev()
Changes from v5:
- Rebase these patch-set on v3.19-rc1 and Test it.
1. exynos-ppmu.c
- Change the error value when of_iomap() fail to map the memory
- Remove owner setting of platform_driver
- Add exynos_ppmu_disable() function
2. exynos dts file
- Add PPMU node to Exynos3250-based Monk board
- Remove ppmu_cpu node on Exynos4412-based TRATS2 board and add ppmu_leftbus/rightbus node
Changes from v4:
1. devfreq-event class driver
- Add devfreq_event_get_edev_count() function
- Modify the simple description of devfreq-event framework in devfreq-event.c
- Minimize the usage range of global lock usage in devfreq_event_add_edev()
- Remove '_is_enabled()' function pointer in devfreq_event_ops structure
- Add separte CONFIG_PM_DEVFREQ_EVENT configuration
- Add new devfreq-event.h header file including devfreq-event helper functions
2. exynos dts file
- Add new patch to support PPMU with DEVFREQ-event on Exynos4412-based TRATS2
Changes from v3:
1. devfreq-event class driver
- Fix return value of devfreq_event_get_event()
- Add new structure devfreq_event_data for devfreq_event_get_event()
- Modify the prototype of devfreq_event_get_event() function
- Call of_node_put after calling of_parse_phandle() to decrement refcount
2. exynos-ppmu driver
- Modify usage of devfreq_event_get_event() function
according to new prototype of this funciton
- Add the additional description to exynos-ppmu.txt how to add PPMU node
in board dts file
- Use 'PPMU_EVENT' macro to remove duplicate codes
- Add the support of PPMU for Exynos5260
3. exynos dts file
- Add missing PPMU_FSYS node to exynos3250.dtsi
- Fix 'ppmu_mfc_l' node name as 'ppmu_mfc' because exynos3250 has only one MFC IP.
- Add missing PPMU_ACP/G3D to exynos4.dtsi
4. etc
- Fix wrong abbreviation of PPMU (PPMU :Platform Performance Monitoring Unit)
- Add new patch to support the PPMU of Exynos5260 SoC
Changes from v2:
1. devfreq-event class driver
- Rename all the helper functions of devfreq-event device
- Add devfreq_event_remove_edev() instead of devfreq_put_event_dev()
- Add devfreq_event_release_edev() to initialize it before put device
- Add the detailed description of devfreq-event API
- Add the attributes of devfreq-event class (enable_count)
- Check the overflow about event/total_event data in devfreq_event_get_event()
- Remove the 'exclusive flag' feature
- Set set_event()/get_event() functions as mandary
- Add missing of_node_put() call
- Change variable type of 'get_event()' funciton from 'int' to 'u64'
2. exynos-ppmu driver
- Remove un-used field (struct devfreq)
- Use 'of_get_child_by_name()' instead of 'of_find_node_by_name()'
- Add missing of_node_put() call
- Fix wrong clock control
- Use devfreq_event_remove_edev() instead of devfreq_remove_device()
- Add the documentation for exynos-ppmu driver
- Remove 'enable/disable/is_enabled/reset' function of exynos-ppmu driver
3. exynos3250-rinato.dts
- Add ppmu_{leftbus|rightbus} dt node and remove ppmu_cpu dt node
Changes from v1:
- Code clean
- Add the description of devfreq-event structure
- Add 'is_enabled' function to devfreq_event_ops structure
- Add 'enable_count' field to devfreq_event_dev structure
- Check whether devfreq-event device is enabled or not
during calling devfreq_event API
- Define the type of devfreq-event device as following
: DEVFREQ_EVENT_TYPE_RAW_DATA
: DEVFREQ_EVENT_TYPE_UTILIZATION
: DEVFREQ_EVENT_TYPE_BANDWIDTH
: DEVFREQ_EVENT_TYPE_LATENCY
- Add the exclusive feature of devfreq-event device.
If devfreq-event device is used on only on devfreq driver,
should used 'devfreq_enable_event_dev_exclusive()' function
- Add new patch6 for test on Exynos3250-based Rinato board
Chanwoo Choi (10):
devfreq: event: Add new devfreq_event class to provide basic data for devfreq governor
devfreq: event: Add the list of supported devfreq-event type
devfreq: event: Add resource-managed function for devfreq-event device
devfreq: event: Add exynos-ppmu devfreq-event driver
devfreq: event: Add documentation for exynos-ppmu devfreq-event driver
ARM: dts: Add PPMU dt node for Exynos3250 SoC
ARM: dts: Add PPMU dt node for Exynos4 SoCs
ARM: dts: Add PPMU dt node for Exynos5260 SoC
ARM: dts: exynos: Add PPMU node to Exynos3250-based Rinato/Monk board
ARM: dts: exynos: Add PPMU node for Exynos4412-based TRATS2 board
.../bindings/devfreq/event/exynos-ppmu.txt | 110 ++++
arch/arm/boot/dts/exynos3250-monk.dts | 40 ++
arch/arm/boot/dts/exynos3250-rinato.dts | 40 ++
arch/arm/boot/dts/exynos3250.dtsi | 74 +++
arch/arm/boot/dts/exynos4.dtsi | 108 ++++
arch/arm/boot/dts/exynos4210.dtsi | 8 +
arch/arm/boot/dts/exynos4412-trats2.dts | 40 ++
arch/arm/boot/dts/exynos5260.dtsi | 90 ++++
drivers/devfreq/Kconfig | 2 +
drivers/devfreq/Makefile | 6 +-
drivers/devfreq/devfreq-event.c | 573 +++++++++++++++++++++
drivers/devfreq/event/Kconfig | 25 +
drivers/devfreq/event/Makefile | 2 +
drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++
include/linux/devfreq-event.h | 205 ++++++++
15 files changed, 1721 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/devfreq/event/exynos-ppmu.txt
create mode 100644 drivers/devfreq/devfreq-event.c
create mode 100644 drivers/devfreq/event/Kconfig
create mode 100644 drivers/devfreq/event/Makefile
create mode 100644 drivers/devfreq/event/exynos-ppmu.c
create mode 100644 include/linux/devfreq-event.h
--
1.8.5.5
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver 2015-01-07 23:51 [PATCHv7 00/10] devfreq: Add devfreq-event class to provide raw data for devfreq device Chanwoo Choi @ 2015-01-07 23:51 ` Chanwoo Choi 0 siblings, 0 replies; 6+ messages in thread From: Chanwoo Choi @ 2015-01-07 23:51 UTC (permalink / raw) To: myungjoo.ham, kgene Cc: kyungmin.park, rafael.j.wysocki, mark.rutland, a.kesavan, tomasz.figa, k.kozlowski, b.zolnierkie, robh+dt, cw00.choi, inki.dae, linux-pm, linux-kernel, linux-arm-kernel, linux-samsung-soc This patch adds exynos-ppmu devfreq-event driver to get performance data of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide useful information about the behavior of the SoC that you can use when analyzing system performance, and made visible and can be counted using logic in each IP. This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c Cc: MyungJoo Ham <myungjoo.ham@samsung.com> Cc: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> --- drivers/devfreq/event/Kconfig | 9 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+) create mode 100644 drivers/devfreq/event/exynos-ppmu.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index 1ced42c..a11720a 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -13,4 +13,13 @@ menuconfig PM_DEVFREQ_EVENT if PM_DEVFREQ_EVENT +config DEVFREQ_EVENT_EXYNOS_PPMU + bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" + depends on ARCH_EXYNOS + select PM_OPP + help + This add the devfreq-event driver for Exynos SoC. It provides PPMU + (Platform Performance Monitoring Unit) counters to estimate the + utilization of each module. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index dc56005..be146ea 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -1 +1,2 @@ # Exynos DEVFREQ Event Drivers +obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c new file mode 100644 index 0000000..b38ab6e --- /dev/null +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -0,0 +1,399 @@ +/* + * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/suspend.h> +#include <linux/devfreq-event.h> + +#define PPMU_ENABLE BIT(0) +#define PPMU_DISABLE 0x0 +#define PPMU_CYCLE_RESET BIT(1) +#define PPMU_COUNTER_RESET BIT(2) + +#define PPMU_ENABLE_COUNT0 BIT(0) +#define PPMU_ENABLE_COUNT1 BIT(1) +#define PPMU_ENABLE_COUNT2 BIT(2) +#define PPMU_ENABLE_COUNT3 BIT(3) +#define PPMU_ENABLE_CYCLE BIT(31) + +#define PPMU_CNTENS 0x10 +#define PPMU_FLAG 0x50 +#define PPMU_CCNT_OVERFLOW BIT(31) +#define PPMU_CCNT 0x100 + +#define PPMU_PMCNT0 0x110 +#define PPMU_PMCNT_OFFSET 0x10 +#define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x)) + +#define PPMU_BEVT0SEL 0x1000 +#define PPMU_BEVTSEL_OFFSET 0x100 +#define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (x * PPMU_BEVTSEL_OFFSET)) + +#define RD_DATA_COUNT 0x5 +#define WR_DATA_COUNT 0x6 +#define RDWR_DATA_COUNT 0x7 + +enum ppmu_counter { + PPMU_PMNCNT0 = 0, + PPMU_PMNCNT1, + PPMU_PMNCNT2, + PPMU_PMNCNT3, + + PPMU_PMNCNT_MAX, +}; + +struct exynos_ppmu_data { + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + unsigned int num_events; + + struct device *dev; + struct clk *clk_ppmu; + struct mutex lock; + + struct __exynos_ppmu { + void __iomem *base; + unsigned int event[PPMU_PMNCNT_MAX]; + unsigned int count[PPMU_PMNCNT_MAX]; + bool ccnt_overflow; + bool count_overflow[PPMU_PMNCNT_MAX]; + } ppmu; +}; + +#define PPMU_EVENT(name) \ + { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \ + { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \ + { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \ + { "ppmu-event3-"#name, PPMU_PMNCNT3 } + +struct __exynos_ppmu_events { + char *name; + int id; +} ppmu_events[] = { + /* For Exynos3250, Exynos4 and Exynos5260 */ + PPMU_EVENT(g3d), + PPMU_EVENT(fsys), + + /* For Exynos4 SoCs and Exynos3250 */ + PPMU_EVENT(dmc0), + PPMU_EVENT(dmc1), + PPMU_EVENT(cpu), + PPMU_EVENT(rightbus), + PPMU_EVENT(leftbus), + PPMU_EVENT(lcd0), + PPMU_EVENT(camif), + + /* Only for Exynos3250 and Exynos5260 */ + PPMU_EVENT(mfc), + + /* Only for Exynos4 SoCs */ + PPMU_EVENT(mfc-left), + PPMU_EVENT(mfc-right), + + /* Only for Exynos5260 SoCs */ + PPMU_EVENT(drex0-s0), + PPMU_EVENT(drex0-s1), + PPMU_EVENT(drex1-s0), + PPMU_EVENT(drex1-s1), + PPMU_EVENT(eagle), + PPMU_EVENT(kfc), + PPMU_EVENT(isp), + PPMU_EVENT(fimc), + PPMU_EVENT(gscl), + PPMU_EVENT(mscl), + PPMU_EVENT(fimd0x), + PPMU_EVENT(fimd1x), + { /* sentinel */ }, +}; + +static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) + if (!strcmp(edev->desc->name, ppmu_events[i].name)) + return ppmu_events[i].id; + + return -EINVAL; +} + +static int exynos_ppmu_reset(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + + __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, data->ppmu.base); + __raw_writel(PPMU_ENABLE_CYCLE | + PPMU_ENABLE_COUNT0 | + PPMU_ENABLE_COUNT1 | + PPMU_ENABLE_COUNT2 | + PPMU_ENABLE_COUNT3, + data->ppmu.base + PPMU_CNTENS); + + return 0; +} + +static int exynos_ppmu_disable(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(edev); + + /* Stop monitoring of PPMU IP */ + __raw_writel(PPMU_DISABLE, data->ppmu.base); + + return 0; +} + +static int exynos_ppmu_set_event(struct devfreq_event_dev *edev, + enum devfreq_event_type type) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + if (id < 0) + return id; + + if (type != edev->desc->type) + return -EINVAL; + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(edev); + + /* Setup count registers to monitor read/write transactions */ + __raw_writel(RDWR_DATA_COUNT, data->ppmu.base + PPMU_BEVTSEL(id)); + + /* Start monitoring of PPMU IP */ + __raw_writel(PPMU_ENABLE, data->ppmu.base); + + return 0; +} + +static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, + enum devfreq_event_type type, + struct devfreq_event_data *edata) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + if (id < 0) + return -EINVAL; + + if (type != edev->desc->type) + return -EINVAL; + + /* Stop monitoring of PPMU IP */ + __raw_writel(PPMU_DISABLE, data->ppmu.base); + + /* Read total count cycle from of PPMU IP */ + edata->total_event = __raw_readl(data->ppmu.base + PPMU_CCNT); + + if (id == PPMU_PMNCNT3) + edata->event = + ((__raw_readl(data->ppmu.base + PMCNT_OFFSET(id)) << 8) + | __raw_readl(data->ppmu.base + PMCNT_OFFSET(id + 1))); + else + edata->event = __raw_readl(data->ppmu.base + PMCNT_OFFSET(id)); + + dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, + edata->event, edata->total_event); + + return 0; +} + +static struct devfreq_event_ops exynos_ppmu_ops = { + .disable = exynos_ppmu_disable, + .set_event = exynos_ppmu_set_event, + .get_event = exynos_ppmu_get_event, +}; + +static int of_get_devfreq_events(struct device_node *np, + struct exynos_ppmu_data *data) +{ + struct devfreq_event_desc *desc; + struct device *dev = data->dev; + struct device_node *events_np, *node; + int i, j, count; + + events_np = of_get_child_by_name(np, "events"); + if (!events_np) { + dev_err(dev, + "failed to get child node of devfreq-event devices\n"); + return -EINVAL; + } + + count = of_get_child_count(events_np); + desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); + if (!desc) + return -ENOMEM; + data->num_events = count; + + j = 0; + for_each_child_of_node(events_np, node) { + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { + if (!ppmu_events[i].name) + continue; + + if (!of_node_cmp(node->name, ppmu_events[i].name)) + break; + } + + if (i == ARRAY_SIZE(ppmu_events)) { + dev_warn(dev, + "don't know how to configure events : %s\n", + node->name); + continue; + } + + desc[j].ops = &exynos_ppmu_ops; + desc[j].driver_data = data; + desc[j].type |= DEVFREQ_EVENT_TYPE_RAW_DATA; + + of_property_read_string(node, "event-name", &desc[j].name); + + j++; + + of_node_put(node); + } + data->desc = desc; + + of_node_put(events_np); + + return 0; +} + +static int exynos_ppmu_parse_dt(struct exynos_ppmu_data *data) +{ + struct device *dev = data->dev; + struct device_node *np = dev->of_node; + int ret = 0; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + /* Maps the memory mapped IO to control PPMU register */ + data->ppmu.base = of_iomap(np, 0); + if (IS_ERR_OR_NULL(data->ppmu.base)) { + dev_err(dev, "failed to map memory region\n"); + return -ENOMEM; + } + + data->clk_ppmu = devm_clk_get(dev, "ppmu"); + if (IS_ERR(data->clk_ppmu)) { + data->clk_ppmu = NULL; + dev_warn(dev, "cannot get PPMU clock\n"); + } + + ret = of_get_devfreq_events(np, data); + if (ret < 0) { + dev_err(dev, "failed to parse exynos ppmu dt node\n"); + goto err; + } + + return 0; + +err: + iounmap(data->ppmu.base); + + return ret; +} + +static int exynos_ppmu_probe(struct platform_device *pdev) +{ + struct exynos_ppmu_data *data; + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + int i, ret = 0, size; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->lock); + data->dev = &pdev->dev; + + /* Parse dt data to get resource */ + ret = exynos_ppmu_parse_dt(data); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to parse devicetree for resource\n"); + return ret; + } + desc = data->desc; + + size = sizeof(struct devfreq_event_dev *) * data->num_events; + data->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!data->edev) { + dev_err(&pdev->dev, + "failed to allocate memory devfreq-event devices\n"); + return -ENOMEM; + } + edev = data->edev; + platform_set_drvdata(pdev, data); + + for (i = 0; i < data->num_events; i++) { + edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); + if (IS_ERR(edev)) { + ret = PTR_ERR(edev); + dev_err(&pdev->dev, + "failed to add devfreq-event device\n"); + goto err; + } + } + + clk_prepare_enable(data->clk_ppmu); + + return 0; +err: + iounmap(data->ppmu.base); + + return ret; +} + +static int exynos_ppmu_remove(struct platform_device *pdev) +{ + struct exynos_ppmu_data *data = platform_get_drvdata(pdev); + + clk_disable_unprepare(data->clk_ppmu); + iounmap(data->ppmu.base); + + return 0; +} + +static struct of_device_id exynos_ppmu_id_match[] = { + { .compatible = "samsung,exynos-ppmu", }, + { /* sentinel */ }, +}; + +static struct platform_driver exynos_ppmu_driver = { + .probe = exynos_ppmu_probe, + .remove = exynos_ppmu_remove, + .driver = { + .name = "exynos-ppmu", + .of_match_table = exynos_ppmu_id_match, + }, +}; +module_platform_driver(exynos_ppmu_driver); + +MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_LICENSE("GPL"); -- 1.8.5.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver @ 2015-01-07 23:51 ` Chanwoo Choi 0 siblings, 0 replies; 6+ messages in thread From: Chanwoo Choi @ 2015-01-07 23:51 UTC (permalink / raw) To: linux-arm-kernel This patch adds exynos-ppmu devfreq-event driver to get performance data of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide useful information about the behavior of the SoC that you can use when analyzing system performance, and made visible and can be counted using logic in each IP. This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c Cc: MyungJoo Ham <myungjoo.ham@samsung.com> Cc: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> --- drivers/devfreq/event/Kconfig | 9 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/exynos-ppmu.c | 399 ++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+) create mode 100644 drivers/devfreq/event/exynos-ppmu.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index 1ced42c..a11720a 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -13,4 +13,13 @@ menuconfig PM_DEVFREQ_EVENT if PM_DEVFREQ_EVENT +config DEVFREQ_EVENT_EXYNOS_PPMU + bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" + depends on ARCH_EXYNOS + select PM_OPP + help + This add the devfreq-event driver for Exynos SoC. It provides PPMU + (Platform Performance Monitoring Unit) counters to estimate the + utilization of each module. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index dc56005..be146ea 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -1 +1,2 @@ # Exynos DEVFREQ Event Drivers +obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c new file mode 100644 index 0000000..b38ab6e --- /dev/null +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -0,0 +1,399 @@ +/* + * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/suspend.h> +#include <linux/devfreq-event.h> + +#define PPMU_ENABLE BIT(0) +#define PPMU_DISABLE 0x0 +#define PPMU_CYCLE_RESET BIT(1) +#define PPMU_COUNTER_RESET BIT(2) + +#define PPMU_ENABLE_COUNT0 BIT(0) +#define PPMU_ENABLE_COUNT1 BIT(1) +#define PPMU_ENABLE_COUNT2 BIT(2) +#define PPMU_ENABLE_COUNT3 BIT(3) +#define PPMU_ENABLE_CYCLE BIT(31) + +#define PPMU_CNTENS 0x10 +#define PPMU_FLAG 0x50 +#define PPMU_CCNT_OVERFLOW BIT(31) +#define PPMU_CCNT 0x100 + +#define PPMU_PMCNT0 0x110 +#define PPMU_PMCNT_OFFSET 0x10 +#define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x)) + +#define PPMU_BEVT0SEL 0x1000 +#define PPMU_BEVTSEL_OFFSET 0x100 +#define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (x * PPMU_BEVTSEL_OFFSET)) + +#define RD_DATA_COUNT 0x5 +#define WR_DATA_COUNT 0x6 +#define RDWR_DATA_COUNT 0x7 + +enum ppmu_counter { + PPMU_PMNCNT0 = 0, + PPMU_PMNCNT1, + PPMU_PMNCNT2, + PPMU_PMNCNT3, + + PPMU_PMNCNT_MAX, +}; + +struct exynos_ppmu_data { + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + unsigned int num_events; + + struct device *dev; + struct clk *clk_ppmu; + struct mutex lock; + + struct __exynos_ppmu { + void __iomem *base; + unsigned int event[PPMU_PMNCNT_MAX]; + unsigned int count[PPMU_PMNCNT_MAX]; + bool ccnt_overflow; + bool count_overflow[PPMU_PMNCNT_MAX]; + } ppmu; +}; + +#define PPMU_EVENT(name) \ + { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \ + { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \ + { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \ + { "ppmu-event3-"#name, PPMU_PMNCNT3 } + +struct __exynos_ppmu_events { + char *name; + int id; +} ppmu_events[] = { + /* For Exynos3250, Exynos4 and Exynos5260 */ + PPMU_EVENT(g3d), + PPMU_EVENT(fsys), + + /* For Exynos4 SoCs and Exynos3250 */ + PPMU_EVENT(dmc0), + PPMU_EVENT(dmc1), + PPMU_EVENT(cpu), + PPMU_EVENT(rightbus), + PPMU_EVENT(leftbus), + PPMU_EVENT(lcd0), + PPMU_EVENT(camif), + + /* Only for Exynos3250 and Exynos5260 */ + PPMU_EVENT(mfc), + + /* Only for Exynos4 SoCs */ + PPMU_EVENT(mfc-left), + PPMU_EVENT(mfc-right), + + /* Only for Exynos5260 SoCs */ + PPMU_EVENT(drex0-s0), + PPMU_EVENT(drex0-s1), + PPMU_EVENT(drex1-s0), + PPMU_EVENT(drex1-s1), + PPMU_EVENT(eagle), + PPMU_EVENT(kfc), + PPMU_EVENT(isp), + PPMU_EVENT(fimc), + PPMU_EVENT(gscl), + PPMU_EVENT(mscl), + PPMU_EVENT(fimd0x), + PPMU_EVENT(fimd1x), + { /* sentinel */ }, +}; + +static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) + if (!strcmp(edev->desc->name, ppmu_events[i].name)) + return ppmu_events[i].id; + + return -EINVAL; +} + +static int exynos_ppmu_reset(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + + __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, data->ppmu.base); + __raw_writel(PPMU_ENABLE_CYCLE | + PPMU_ENABLE_COUNT0 | + PPMU_ENABLE_COUNT1 | + PPMU_ENABLE_COUNT2 | + PPMU_ENABLE_COUNT3, + data->ppmu.base + PPMU_CNTENS); + + return 0; +} + +static int exynos_ppmu_disable(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(edev); + + /* Stop monitoring of PPMU IP */ + __raw_writel(PPMU_DISABLE, data->ppmu.base); + + return 0; +} + +static int exynos_ppmu_set_event(struct devfreq_event_dev *edev, + enum devfreq_event_type type) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + if (id < 0) + return id; + + if (type != edev->desc->type) + return -EINVAL; + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(edev); + + /* Setup count registers to monitor read/write transactions */ + __raw_writel(RDWR_DATA_COUNT, data->ppmu.base + PPMU_BEVTSEL(id)); + + /* Start monitoring of PPMU IP */ + __raw_writel(PPMU_ENABLE, data->ppmu.base); + + return 0; +} + +static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, + enum devfreq_event_type type, + struct devfreq_event_data *edata) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + if (id < 0) + return -EINVAL; + + if (type != edev->desc->type) + return -EINVAL; + + /* Stop monitoring of PPMU IP */ + __raw_writel(PPMU_DISABLE, data->ppmu.base); + + /* Read total count cycle from of PPMU IP */ + edata->total_event = __raw_readl(data->ppmu.base + PPMU_CCNT); + + if (id == PPMU_PMNCNT3) + edata->event = + ((__raw_readl(data->ppmu.base + PMCNT_OFFSET(id)) << 8) + | __raw_readl(data->ppmu.base + PMCNT_OFFSET(id + 1))); + else + edata->event = __raw_readl(data->ppmu.base + PMCNT_OFFSET(id)); + + dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, + edata->event, edata->total_event); + + return 0; +} + +static struct devfreq_event_ops exynos_ppmu_ops = { + .disable = exynos_ppmu_disable, + .set_event = exynos_ppmu_set_event, + .get_event = exynos_ppmu_get_event, +}; + +static int of_get_devfreq_events(struct device_node *np, + struct exynos_ppmu_data *data) +{ + struct devfreq_event_desc *desc; + struct device *dev = data->dev; + struct device_node *events_np, *node; + int i, j, count; + + events_np = of_get_child_by_name(np, "events"); + if (!events_np) { + dev_err(dev, + "failed to get child node of devfreq-event devices\n"); + return -EINVAL; + } + + count = of_get_child_count(events_np); + desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); + if (!desc) + return -ENOMEM; + data->num_events = count; + + j = 0; + for_each_child_of_node(events_np, node) { + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { + if (!ppmu_events[i].name) + continue; + + if (!of_node_cmp(node->name, ppmu_events[i].name)) + break; + } + + if (i == ARRAY_SIZE(ppmu_events)) { + dev_warn(dev, + "don't know how to configure events : %s\n", + node->name); + continue; + } + + desc[j].ops = &exynos_ppmu_ops; + desc[j].driver_data = data; + desc[j].type |= DEVFREQ_EVENT_TYPE_RAW_DATA; + + of_property_read_string(node, "event-name", &desc[j].name); + + j++; + + of_node_put(node); + } + data->desc = desc; + + of_node_put(events_np); + + return 0; +} + +static int exynos_ppmu_parse_dt(struct exynos_ppmu_data *data) +{ + struct device *dev = data->dev; + struct device_node *np = dev->of_node; + int ret = 0; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + /* Maps the memory mapped IO to control PPMU register */ + data->ppmu.base = of_iomap(np, 0); + if (IS_ERR_OR_NULL(data->ppmu.base)) { + dev_err(dev, "failed to map memory region\n"); + return -ENOMEM; + } + + data->clk_ppmu = devm_clk_get(dev, "ppmu"); + if (IS_ERR(data->clk_ppmu)) { + data->clk_ppmu = NULL; + dev_warn(dev, "cannot get PPMU clock\n"); + } + + ret = of_get_devfreq_events(np, data); + if (ret < 0) { + dev_err(dev, "failed to parse exynos ppmu dt node\n"); + goto err; + } + + return 0; + +err: + iounmap(data->ppmu.base); + + return ret; +} + +static int exynos_ppmu_probe(struct platform_device *pdev) +{ + struct exynos_ppmu_data *data; + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + int i, ret = 0, size; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->lock); + data->dev = &pdev->dev; + + /* Parse dt data to get resource */ + ret = exynos_ppmu_parse_dt(data); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to parse devicetree for resource\n"); + return ret; + } + desc = data->desc; + + size = sizeof(struct devfreq_event_dev *) * data->num_events; + data->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!data->edev) { + dev_err(&pdev->dev, + "failed to allocate memory devfreq-event devices\n"); + return -ENOMEM; + } + edev = data->edev; + platform_set_drvdata(pdev, data); + + for (i = 0; i < data->num_events; i++) { + edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); + if (IS_ERR(edev)) { + ret = PTR_ERR(edev); + dev_err(&pdev->dev, + "failed to add devfreq-event device\n"); + goto err; + } + } + + clk_prepare_enable(data->clk_ppmu); + + return 0; +err: + iounmap(data->ppmu.base); + + return ret; +} + +static int exynos_ppmu_remove(struct platform_device *pdev) +{ + struct exynos_ppmu_data *data = platform_get_drvdata(pdev); + + clk_disable_unprepare(data->clk_ppmu); + iounmap(data->ppmu.base); + + return 0; +} + +static struct of_device_id exynos_ppmu_id_match[] = { + { .compatible = "samsung,exynos-ppmu", }, + { /* sentinel */ }, +}; + +static struct platform_driver exynos_ppmu_driver = { + .probe = exynos_ppmu_probe, + .remove = exynos_ppmu_remove, + .driver = { + .name = "exynos-ppmu", + .of_match_table = exynos_ppmu_id_match, + }, +}; +module_platform_driver(exynos_ppmu_driver); + +MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_LICENSE("GPL"); -- 1.8.5.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2015-01-12 8:39 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-01-12 8:04 [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver MyungJoo Ham 2015-01-12 8:04 ` MyungJoo Ham 2015-01-12 8:39 ` Chanwoo Choi 2015-01-12 8:39 ` Chanwoo Choi -- strict thread matches above, loose matches on Subject: below -- 2015-01-07 23:51 [PATCHv7 00/10] devfreq: Add devfreq-event class to provide raw data for devfreq device Chanwoo Choi 2015-01-07 23:51 ` [PATCHv7 04/10] devfreq: event: Add exynos-ppmu devfreq-event driver Chanwoo Choi 2015-01-07 23:51 ` Chanwoo Choi
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.