diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-nocp.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-nocp.txt new file mode 100644 index 000000000000..7266a9c65ada --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-nocp.txt @@ -0,0 +1,23 @@ + +* Rockchip NoC (Network on Chip) Probe device + +The Rockchip SoCs have NoC (Network on Chip) Probe for NoC bus. +NoC provides the primitive values to get the performance data, The +packets that the Network on Chip (NoC) probes detects are transported +over the network infrastructure to observer units. For example, RK3399 +has multiple NoC probes to monitor traffic statistics for analyzing the +transaction flow. + +Required properties: +- compatible: Should be one of the following. + - "rockchip,rk3288-nocp" - for RK3288 SoC. + - "rockchip,rk3368-nocp" - for RK3368 SoC. + - "rockchip,rk3399-nocp" - for RK3399 SoC. +- reg: physical base address of each NoC Probe and length of memory mapped region. + +Example : NoC Probe nodes in Device Tree are listed below. + + nocp_cci_msch0: nocp-cci-msch0@ffa86000 { + compatible = "rockchip,rk3399-nocp"; + reg = <0xffa86000 0x400>; + }; diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index ff9279fcc1f5..8700b2dd60b8 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -29,4 +29,11 @@ config DEVFREQ_EVENT_ROCKCHIP_DFI This add the devfreq-event driver for Rockchip SoC. It provides DFI (DDR Monitor Module) driver to count ddr load. +config DEVFREQ_EVENT_ROCKCHIP_NOCP + tristate "ROCKCHIP NoC (Network On Chip) Probe DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides NoC + (Network on Chip) Probe counters to monitor traffic statistics. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index e3f88fc43141..8362da70fa56 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -1,3 +1,4 @@ # Exynos DEVFREQ Event Drivers obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_NOCP) += rockchip-nocp.o diff --git a/drivers/devfreq/event/rockchip-nocp.c b/drivers/devfreq/event/rockchip-nocp.c new file mode 100644 index 000000000000..957b84ee3290 --- /dev/null +++ b/drivers/devfreq/event/rockchip-nocp.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define EVENT_BYTE 0x08 +#define EVENT_CHAIN 0x10 + +#define START_EN BIT(3) +#define GLOBAL_EN BIT(0) +#define START_GO BIT(0) + +#define PROBE_MAINCTL 0x0008 +#define PROBE_CFGCTL 0x000c +#define PROBE_STATPERIOD 0x0024 +#define PROBE_STATGO 0x0028 +#define PROBE_COUNTERS_0_SRC 0x0138 +#define PROBE_COUNTERS_0_VAL 0x013c +#define PROBE_COUNTERS_1_SRC 0x014c +#define PROBE_COUNTERS_1_VAL 0x0150 + +struct rockchip_nocp { + void __iomem *reg_base; + struct device *dev; + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + ktime_t time; +}; + +static int rockchip_nocp_enable(struct devfreq_event_dev *edev) +{ + struct rockchip_nocp *nocp = devfreq_event_get_drvdata(edev); + void __iomem *reg_base = nocp->reg_base; + + writel_relaxed(GLOBAL_EN, reg_base + PROBE_CFGCTL); + writel_relaxed(START_EN, reg_base + PROBE_MAINCTL); + writel_relaxed(0, reg_base + PROBE_STATPERIOD); + writel_relaxed(EVENT_BYTE, reg_base + PROBE_COUNTERS_0_SRC); + writel_relaxed(EVENT_CHAIN, reg_base + PROBE_COUNTERS_1_SRC); + writel_relaxed(START_GO, reg_base + PROBE_STATGO); + + nocp->time = ktime_get(); + + return 0; +} + +static int rockchip_nocp_disable(struct devfreq_event_dev *edev) +{ + struct rockchip_nocp *nocp = devfreq_event_get_drvdata(edev); + void __iomem *reg_base = nocp->reg_base; + + writel_relaxed(0, reg_base + PROBE_STATGO); + writel_relaxed(0, reg_base + PROBE_MAINCTL); + writel_relaxed(0, reg_base + PROBE_CFGCTL); + writel_relaxed(0, reg_base + PROBE_COUNTERS_0_SRC); + writel_relaxed(0, reg_base + PROBE_COUNTERS_1_SRC); + + return 0; +} + +static int rockchip_nocp_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + struct rockchip_nocp *nocp = devfreq_event_get_drvdata(edev); + void __iomem *reg_base = nocp->reg_base; + u32 counter = 0, counter0 = 0, counter1 = 0; + int time_ms = 0; + + time_ms = ktime_to_ms(ktime_sub(ktime_get(), nocp->time)); + + counter0 = readl_relaxed(reg_base + PROBE_COUNTERS_0_VAL); + counter1 = readl_relaxed(reg_base + PROBE_COUNTERS_1_VAL); + counter = (counter0 & 0xffff) | ((counter1 & 0xffff) << 16); + counter = counter / 1000000; + if (time_ms > 0) + edata->load_count = (counter * 1000) / time_ms; + + writel_relaxed(START_GO, reg_base + PROBE_STATGO); + nocp->time = ktime_get(); + + return 0; +} + +static int rockchip_nocp_set_event(struct devfreq_event_dev *edev) +{ + return 0; +} + +static const struct devfreq_event_ops rockchip_nocp_ops = { + .disable = rockchip_nocp_disable, + .enable = rockchip_nocp_enable, + .get_event = rockchip_nocp_get_event, + .set_event = rockchip_nocp_set_event, +}; + +static const struct of_device_id rockchip_nocp_id_match[] = { + { .compatible = "rockchip,rk3288-nocp" }, + { .compatible = "rockchip,rk3368-nocp" }, + { .compatible = "rockchip,rk3399-nocp" }, + { }, +}; + +static int rockchip_nocp_probe(struct platform_device *pdev) +{ + struct resource *res; + struct rockchip_nocp *nocp; + struct devfreq_event_desc *desc; + struct device_node *np = pdev->dev.of_node; + + nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL); + if (!nocp) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nocp->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nocp->reg_base)) + return PTR_ERR(nocp->reg_base); + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + desc->ops = &rockchip_nocp_ops; + desc->driver_data = nocp; + desc->name = np->name; + nocp->desc = desc; + nocp->dev = &pdev->dev; + nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, desc); + if (IS_ERR(nocp->edev)) { + dev_err(&pdev->dev, "failed to add devfreq-event device\n"); + return PTR_ERR(nocp->edev); + } + + platform_set_drvdata(pdev, nocp); + + return 0; +} + +static struct platform_driver rockchip_nocp_driver = { + .probe = rockchip_nocp_probe, + .driver = { + .name = "rockchip-nocp", + .of_match_table = rockchip_nocp_id_match, + }, +}; +module_platform_driver(rockchip_nocp_driver); + +MODULE_DESCRIPTION("Rockchip NoC (Network on Chip) Probe driver"); +MODULE_AUTHOR("Finley Xiao "); +MODULE_LICENSE("GPL v2");