13d0407baSopenharmony_ci/* 23d0407baSopenharmony_ci * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd 33d0407baSopenharmony_ci * 43d0407baSopenharmony_ci * This program is free software; you can redistribute it and/or modify it 53d0407baSopenharmony_ci * under the terms and conditions of the GNU General Public License, 63d0407baSopenharmony_ci * version 2, as published by the Free Software Foundation. 73d0407baSopenharmony_ci * 83d0407baSopenharmony_ci * This program is distributed in the hope it will be useful, but WITHOUT 93d0407baSopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 103d0407baSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 113d0407baSopenharmony_ci * more details. 123d0407baSopenharmony_ci */ 133d0407baSopenharmony_ci 143d0407baSopenharmony_ci#include <linux/devfreq-event.h> 153d0407baSopenharmony_ci#include <linux/io.h> 163d0407baSopenharmony_ci#include <linux/kernel.h> 173d0407baSopenharmony_ci#include <linux/mfd/syscon.h> 183d0407baSopenharmony_ci#include <linux/module.h> 193d0407baSopenharmony_ci#include <linux/of.h> 203d0407baSopenharmony_ci#include <linux/platform_device.h> 213d0407baSopenharmony_ci 223d0407baSopenharmony_ci#define EVENT_BYTE 0x08 233d0407baSopenharmony_ci#define EVENT_CHAIN 0x10 243d0407baSopenharmony_ci 253d0407baSopenharmony_ci#define START_EN BIT(3) 263d0407baSopenharmony_ci#define GLOBAL_EN BIT(0) 273d0407baSopenharmony_ci#define START_GO BIT(0) 283d0407baSopenharmony_ci 293d0407baSopenharmony_ci#define PROBE_MAINCTL 0x0008 303d0407baSopenharmony_ci#define PROBE_CFGCTL 0x000c 313d0407baSopenharmony_ci#define PROBE_STATPERIOD 0x0024 323d0407baSopenharmony_ci#define PROBE_STATGO 0x0028 333d0407baSopenharmony_ci#define PROBE_COUNTERS_0_SRC 0x0138 343d0407baSopenharmony_ci#define PROBE_COUNTERS_0_VAL 0x013c 353d0407baSopenharmony_ci#define PROBE_COUNTERS_1_SRC 0x014c 363d0407baSopenharmony_ci#define PROBE_COUNTERS_1_VAL 0x0150 373d0407baSopenharmony_ci 383d0407baSopenharmony_cistruct rockchip_nocp { 393d0407baSopenharmony_ci void __iomem *reg_base; 403d0407baSopenharmony_ci struct device *dev; 413d0407baSopenharmony_ci struct devfreq_event_dev *edev; 423d0407baSopenharmony_ci struct devfreq_event_desc *desc; 433d0407baSopenharmony_ci ktime_t time; 443d0407baSopenharmony_ci}; 453d0407baSopenharmony_ci 463d0407baSopenharmony_cistatic int rockchip_nocp_enable(struct devfreq_event_dev *edev) 473d0407baSopenharmony_ci{ 483d0407baSopenharmony_ci struct rockchip_nocp *nocp = devfreq_event_get_drvdata(edev); 493d0407baSopenharmony_ci void __iomem *reg_base = nocp->reg_base; 503d0407baSopenharmony_ci 513d0407baSopenharmony_ci writel_relaxed(GLOBAL_EN, reg_base + PROBE_CFGCTL); 523d0407baSopenharmony_ci writel_relaxed(START_EN, reg_base + PROBE_MAINCTL); 533d0407baSopenharmony_ci writel_relaxed(0, reg_base + PROBE_STATPERIOD); 543d0407baSopenharmony_ci writel_relaxed(EVENT_BYTE, reg_base + PROBE_COUNTERS_0_SRC); 553d0407baSopenharmony_ci writel_relaxed(EVENT_CHAIN, reg_base + PROBE_COUNTERS_1_SRC); 563d0407baSopenharmony_ci writel_relaxed(START_GO, reg_base + PROBE_STATGO); 573d0407baSopenharmony_ci 583d0407baSopenharmony_ci nocp->time = ktime_get(); 593d0407baSopenharmony_ci 603d0407baSopenharmony_ci return 0; 613d0407baSopenharmony_ci} 623d0407baSopenharmony_ci 633d0407baSopenharmony_cistatic int rockchip_nocp_disable(struct devfreq_event_dev *edev) 643d0407baSopenharmony_ci{ 653d0407baSopenharmony_ci struct rockchip_nocp *nocp = devfreq_event_get_drvdata(edev); 663d0407baSopenharmony_ci void __iomem *reg_base = nocp->reg_base; 673d0407baSopenharmony_ci 683d0407baSopenharmony_ci writel_relaxed(0, reg_base + PROBE_STATGO); 693d0407baSopenharmony_ci writel_relaxed(0, reg_base + PROBE_MAINCTL); 703d0407baSopenharmony_ci writel_relaxed(0, reg_base + PROBE_CFGCTL); 713d0407baSopenharmony_ci writel_relaxed(0, reg_base + PROBE_COUNTERS_0_SRC); 723d0407baSopenharmony_ci writel_relaxed(0, reg_base + PROBE_COUNTERS_1_SRC); 733d0407baSopenharmony_ci 743d0407baSopenharmony_ci return 0; 753d0407baSopenharmony_ci} 763d0407baSopenharmony_ci 773d0407baSopenharmony_cistatic int rockchip_nocp_get_event(struct devfreq_event_dev *edev, struct devfreq_event_data *edata) 783d0407baSopenharmony_ci{ 793d0407baSopenharmony_ci struct rockchip_nocp *nocp = devfreq_event_get_drvdata(edev); 803d0407baSopenharmony_ci void __iomem *reg_base = nocp->reg_base; 813d0407baSopenharmony_ci u32 counter = 0, counter0 = 0, counter1 = 0; 823d0407baSopenharmony_ci int time_ms = 0; 833d0407baSopenharmony_ci 843d0407baSopenharmony_ci time_ms = ktime_to_ms(ktime_sub(ktime_get(), nocp->time)); 853d0407baSopenharmony_ci 863d0407baSopenharmony_ci counter0 = readl_relaxed(reg_base + PROBE_COUNTERS_0_VAL); 873d0407baSopenharmony_ci counter1 = readl_relaxed(reg_base + PROBE_COUNTERS_1_VAL); 883d0407baSopenharmony_ci counter = (counter0 & 0xffff) | ((counter1 & 0xffff) << 0x10); 893d0407baSopenharmony_ci counter = counter / 0xf4240; 903d0407baSopenharmony_ci if (time_ms > 0) { 913d0407baSopenharmony_ci edata->load_count = (counter * 0x3e8) / time_ms; 923d0407baSopenharmony_ci } 933d0407baSopenharmony_ci 943d0407baSopenharmony_ci writel_relaxed(START_GO, reg_base + PROBE_STATGO); 953d0407baSopenharmony_ci nocp->time = ktime_get(); 963d0407baSopenharmony_ci 973d0407baSopenharmony_ci return 0; 983d0407baSopenharmony_ci} 993d0407baSopenharmony_ci 1003d0407baSopenharmony_cistatic int rockchip_nocp_set_event(struct devfreq_event_dev *edev) 1013d0407baSopenharmony_ci{ 1023d0407baSopenharmony_ci return 0; 1033d0407baSopenharmony_ci} 1043d0407baSopenharmony_ci 1053d0407baSopenharmony_cistatic const struct devfreq_event_ops rockchip_nocp_ops = { 1063d0407baSopenharmony_ci .disable = rockchip_nocp_disable, 1073d0407baSopenharmony_ci .enable = rockchip_nocp_enable, 1083d0407baSopenharmony_ci .get_event = rockchip_nocp_get_event, 1093d0407baSopenharmony_ci .set_event = rockchip_nocp_set_event, 1103d0407baSopenharmony_ci}; 1113d0407baSopenharmony_ci 1123d0407baSopenharmony_cistatic const struct of_device_id rockchip_nocp_id_match[] = { 1133d0407baSopenharmony_ci {.compatible = "rockchip,rk3288-nocp"}, 1143d0407baSopenharmony_ci {.compatible = "rockchip,rk3368-nocp"}, 1153d0407baSopenharmony_ci {.compatible = "rockchip,rk3399-nocp"}, 1163d0407baSopenharmony_ci {}, 1173d0407baSopenharmony_ci}; 1183d0407baSopenharmony_ci 1193d0407baSopenharmony_cistatic int rockchip_nocp_probe(struct platform_device *pdev) 1203d0407baSopenharmony_ci{ 1213d0407baSopenharmony_ci struct resource *res; 1223d0407baSopenharmony_ci struct rockchip_nocp *nocp; 1233d0407baSopenharmony_ci struct devfreq_event_desc *desc; 1243d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node; 1253d0407baSopenharmony_ci 1263d0407baSopenharmony_ci nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL); 1273d0407baSopenharmony_ci if (!nocp) { 1283d0407baSopenharmony_ci return -ENOMEM; 1293d0407baSopenharmony_ci } 1303d0407baSopenharmony_ci 1313d0407baSopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1323d0407baSopenharmony_ci nocp->reg_base = devm_ioremap_resource(&pdev->dev, res); 1333d0407baSopenharmony_ci if (IS_ERR(nocp->reg_base)) { 1343d0407baSopenharmony_ci return PTR_ERR(nocp->reg_base); 1353d0407baSopenharmony_ci } 1363d0407baSopenharmony_ci 1373d0407baSopenharmony_ci desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); 1383d0407baSopenharmony_ci if (!desc) { 1393d0407baSopenharmony_ci return -ENOMEM; 1403d0407baSopenharmony_ci } 1413d0407baSopenharmony_ci 1423d0407baSopenharmony_ci desc->ops = &rockchip_nocp_ops; 1433d0407baSopenharmony_ci desc->driver_data = nocp; 1443d0407baSopenharmony_ci desc->name = np->name; 1453d0407baSopenharmony_ci nocp->desc = desc; 1463d0407baSopenharmony_ci nocp->dev = &pdev->dev; 1473d0407baSopenharmony_ci nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, desc); 1483d0407baSopenharmony_ci if (IS_ERR(nocp->edev)) { 1493d0407baSopenharmony_ci dev_err(&pdev->dev, "failed to add devfreq-event device\n"); 1503d0407baSopenharmony_ci return PTR_ERR(nocp->edev); 1513d0407baSopenharmony_ci } 1523d0407baSopenharmony_ci 1533d0407baSopenharmony_ci platform_set_drvdata(pdev, nocp); 1543d0407baSopenharmony_ci 1553d0407baSopenharmony_ci return 0; 1563d0407baSopenharmony_ci} 1573d0407baSopenharmony_ci 1583d0407baSopenharmony_cistatic struct platform_driver rockchip_nocp_driver = { 1593d0407baSopenharmony_ci .probe = rockchip_nocp_probe, 1603d0407baSopenharmony_ci .driver = 1613d0407baSopenharmony_ci { 1623d0407baSopenharmony_ci .name = "rockchip-nocp", 1633d0407baSopenharmony_ci .of_match_table = rockchip_nocp_id_match, 1643d0407baSopenharmony_ci }, 1653d0407baSopenharmony_ci}; 1663d0407baSopenharmony_cimodule_platform_driver(rockchip_nocp_driver); 1673d0407baSopenharmony_ci 1683d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip NoC (Network on Chip) Probe driver"); 1693d0407baSopenharmony_ciMODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>"); 1703d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 171