brintos

brintos / linux-shallow public Read only

0
0
Text · 16.5 KiB · 7002df2 Raw
715 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support4 *5 * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.6 * Author : Chanwoo Choi <cw00.choi@samsung.com>7 *8 * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c9 */10 11#include <linux/clk.h>12#include <linux/io.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/property.h>18#include <linux/regmap.h>19#include <linux/suspend.h>20#include <linux/devfreq-event.h>21 22#include "exynos-ppmu.h"23 24enum exynos_ppmu_type {25	EXYNOS_TYPE_PPMU,26	EXYNOS_TYPE_PPMU_V2,27};28 29struct exynos_ppmu_data {30	struct clk *clk;31};32 33struct exynos_ppmu {34	struct devfreq_event_dev **edev;35	struct devfreq_event_desc *desc;36	unsigned int num_events;37 38	struct device *dev;39	struct regmap *regmap;40 41	struct exynos_ppmu_data ppmu;42	enum exynos_ppmu_type ppmu_type;43};44 45#define PPMU_EVENT(name)			\46	{ "ppmu-event0-"#name, PPMU_PMNCNT0 },	\47	{ "ppmu-event1-"#name, PPMU_PMNCNT1 },	\48	{ "ppmu-event2-"#name, PPMU_PMNCNT2 },	\49	{ "ppmu-event3-"#name, PPMU_PMNCNT3 }50 51static struct __exynos_ppmu_events {52	char *name;53	int id;54} ppmu_events[] = {55	/* For Exynos3250, Exynos4 and Exynos5260 */56	PPMU_EVENT(g3d),57	PPMU_EVENT(fsys),58 59	/* For Exynos4 SoCs and Exynos3250 */60	PPMU_EVENT(dmc0),61	PPMU_EVENT(dmc1),62	PPMU_EVENT(cpu),63	PPMU_EVENT(rightbus),64	PPMU_EVENT(leftbus),65	PPMU_EVENT(lcd0),66	PPMU_EVENT(camif),67 68	/* Only for Exynos3250 and Exynos5260 */69	PPMU_EVENT(mfc),70 71	/* Only for Exynos4 SoCs */72	PPMU_EVENT(mfc-left),73	PPMU_EVENT(mfc-right),74 75	/* Only for Exynos5260 SoCs */76	PPMU_EVENT(drex0-s0),77	PPMU_EVENT(drex0-s1),78	PPMU_EVENT(drex1-s0),79	PPMU_EVENT(drex1-s1),80	PPMU_EVENT(eagle),81	PPMU_EVENT(kfc),82	PPMU_EVENT(isp),83	PPMU_EVENT(fimc),84	PPMU_EVENT(gscl),85	PPMU_EVENT(mscl),86	PPMU_EVENT(fimd0x),87	PPMU_EVENT(fimd1x),88 89	/* Only for Exynos5433 SoCs */90	PPMU_EVENT(d0-cpu),91	PPMU_EVENT(d0-general),92	PPMU_EVENT(d0-rt),93	PPMU_EVENT(d1-cpu),94	PPMU_EVENT(d1-general),95	PPMU_EVENT(d1-rt),96 97	/* For Exynos5422 SoC, deprecated (backwards compatible) */98	PPMU_EVENT(dmc0_0),99	PPMU_EVENT(dmc0_1),100	PPMU_EVENT(dmc1_0),101	PPMU_EVENT(dmc1_1),102	/* For Exynos5422 SoC */103	PPMU_EVENT(dmc0-0),104	PPMU_EVENT(dmc0-1),105	PPMU_EVENT(dmc1-0),106	PPMU_EVENT(dmc1-1),107};108 109static int __exynos_ppmu_find_ppmu_id(const char *edev_name)110{111	int i;112 113	for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)114		if (!strcmp(edev_name, ppmu_events[i].name))115			return ppmu_events[i].id;116 117	return -EINVAL;118}119 120static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)121{122	return __exynos_ppmu_find_ppmu_id(edev->desc->name);123}124 125/*126 * The devfreq-event ops structure for PPMU v1.1127 */128static int exynos_ppmu_disable(struct devfreq_event_dev *edev)129{130	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);131	int ret;132	u32 pmnc;133 134	/* Disable all counters */135	ret = regmap_write(info->regmap, PPMU_CNTENC,136				PPMU_CCNT_MASK |137				PPMU_PMCNT0_MASK |138				PPMU_PMCNT1_MASK |139				PPMU_PMCNT2_MASK |140				PPMU_PMCNT3_MASK);141	if (ret < 0)142		return ret;143 144	/* Disable PPMU */145	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);146	if (ret < 0)147		return ret;148 149	pmnc &= ~PPMU_PMNC_ENABLE_MASK;150	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);151	if (ret < 0)152		return ret;153 154	return 0;155}156 157static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)158{159	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);160	int id = exynos_ppmu_find_ppmu_id(edev);161	int ret;162	u32 pmnc, cntens;163 164	if (id < 0)165		return id;166 167	/* Enable specific counter */168	ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);169	if (ret < 0)170		return ret;171 172	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));173	ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);174	if (ret < 0)175		return ret;176 177	/* Set the event of proper data type monitoring */178	ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),179			   edev->desc->event_type);180	if (ret < 0)181		return ret;182 183	/* Reset cycle counter/performance counter and enable PPMU */184	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);185	if (ret < 0)186		return ret;187 188	pmnc &= ~(PPMU_PMNC_ENABLE_MASK189			| PPMU_PMNC_COUNTER_RESET_MASK190			| PPMU_PMNC_CC_RESET_MASK);191	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);192	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);193	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);194	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);195	if (ret < 0)196		return ret;197 198	return 0;199}200 201static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,202				struct devfreq_event_data *edata)203{204	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);205	int id = exynos_ppmu_find_ppmu_id(edev);206	unsigned int total_count, load_count;207	unsigned int pmcnt3_high, pmcnt3_low;208	unsigned int pmnc, cntenc;209	int ret;210 211	if (id < 0)212		return -EINVAL;213 214	/* Disable PPMU */215	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);216	if (ret < 0)217		return ret;218 219	pmnc &= ~PPMU_PMNC_ENABLE_MASK;220	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);221	if (ret < 0)222		return ret;223 224	/* Read cycle count */225	ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);226	if (ret < 0)227		return ret;228	edata->total_count = total_count;229 230	/* Read performance count */231	switch (id) {232	case PPMU_PMNCNT0:233	case PPMU_PMNCNT1:234	case PPMU_PMNCNT2:235		ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);236		if (ret < 0)237			return ret;238		edata->load_count = load_count;239		break;240	case PPMU_PMNCNT3:241		ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);242		if (ret < 0)243			return ret;244 245		ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);246		if (ret < 0)247			return ret;248 249		edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);250		break;251	default:252		return -EINVAL;253	}254 255	/* Disable specific counter */256	ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);257	if (ret < 0)258		return ret;259 260	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));261	ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);262	if (ret < 0)263		return ret;264 265	dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,266					edata->load_count, edata->total_count);267 268	return 0;269}270 271static const struct devfreq_event_ops exynos_ppmu_ops = {272	.disable = exynos_ppmu_disable,273	.set_event = exynos_ppmu_set_event,274	.get_event = exynos_ppmu_get_event,275};276 277/*278 * The devfreq-event ops structure for PPMU v2.0279 */280static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)281{282	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);283	int ret;284	u32 pmnc, clear;285 286	/* Disable all counters */287	clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK288		| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);289	ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);290	if (ret < 0)291		return ret;292 293	ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);294	if (ret < 0)295		return ret;296 297	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);298	if (ret < 0)299		return ret;300 301	ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);302	if (ret < 0)303		return ret;304 305	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);306	if (ret < 0)307		return ret;308 309	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);310	if (ret < 0)311		return ret;312 313	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);314	if (ret < 0)315		return ret;316 317	ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);318	if (ret < 0)319		return ret;320 321	ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);322	if (ret < 0)323		return ret;324 325	ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);326	if (ret < 0)327		return ret;328 329	ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);330	if (ret < 0)331		return ret;332 333	ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);334	if (ret < 0)335		return ret;336 337	ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);338	if (ret < 0)339		return ret;340 341	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);342	if (ret < 0)343		return ret;344 345	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);346	if (ret < 0)347		return ret;348 349	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);350	if (ret < 0)351		return ret;352 353	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);354	if (ret < 0)355		return ret;356 357	ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);358	if (ret < 0)359		return ret;360 361	/* Disable PPMU */362	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);363	if (ret < 0)364		return ret;365 366	pmnc &= ~PPMU_PMNC_ENABLE_MASK;367	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);368	if (ret < 0)369		return ret;370 371	return 0;372}373 374static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)375{376	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);377	unsigned int pmnc, cntens;378	int id = exynos_ppmu_find_ppmu_id(edev);379	int ret;380 381	/* Enable all counters */382	ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);383	if (ret < 0)384		return ret;385 386	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));387	ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);388	if (ret < 0)389		return ret;390 391	/* Set the event of proper data type monitoring */392	ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),393			   edev->desc->event_type);394	if (ret < 0)395		return ret;396 397	/* Reset cycle counter/performance counter and enable PPMU */398	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);399	if (ret < 0)400		return ret;401 402	pmnc &= ~(PPMU_PMNC_ENABLE_MASK403			| PPMU_PMNC_COUNTER_RESET_MASK404			| PPMU_PMNC_CC_RESET_MASK405			| PPMU_PMNC_CC_DIVIDER_MASK406			| PPMU_V2_PMNC_START_MODE_MASK);407	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);408	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);409	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);410	pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);411 412	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);413	if (ret < 0)414		return ret;415 416	return 0;417}418 419static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,420				    struct devfreq_event_data *edata)421{422	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);423	int id = exynos_ppmu_find_ppmu_id(edev);424	int ret;425	unsigned int pmnc, cntenc;426	unsigned int pmcnt_high, pmcnt_low;427	unsigned int total_count, count;428	unsigned long load_count = 0;429 430	/* Disable PPMU */431	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);432	if (ret < 0)433		return ret;434 435	pmnc &= ~PPMU_PMNC_ENABLE_MASK;436	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);437	if (ret < 0)438		return ret;439 440	/* Read cycle count and performance count */441	ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);442	if (ret < 0)443		return ret;444	edata->total_count = total_count;445 446	switch (id) {447	case PPMU_PMNCNT0:448	case PPMU_PMNCNT1:449	case PPMU_PMNCNT2:450		ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);451		if (ret < 0)452			return ret;453		load_count = count;454		break;455	case PPMU_PMNCNT3:456		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,457						&pmcnt_high);458		if (ret < 0)459			return ret;460 461		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);462		if (ret < 0)463			return ret;464 465		load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;466		break;467	}468	edata->load_count = load_count;469 470	/* Disable all counters */471	ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);472	if (ret < 0)473		return 0;474 475	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));476	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);477	if (ret < 0)478		return ret;479 480	dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,481					edata->load_count, edata->total_count);482	return 0;483}484 485static const struct devfreq_event_ops exynos_ppmu_v2_ops = {486	.disable = exynos_ppmu_v2_disable,487	.set_event = exynos_ppmu_v2_set_event,488	.get_event = exynos_ppmu_v2_get_event,489};490 491static const struct of_device_id exynos_ppmu_id_match[] = {492	{493		.compatible = "samsung,exynos-ppmu",494		.data = (void *)EXYNOS_TYPE_PPMU,495	}, {496		.compatible = "samsung,exynos-ppmu-v2",497		.data = (void *)EXYNOS_TYPE_PPMU_V2,498	},499	{ /* sentinel */ },500};501MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);502 503static int of_get_devfreq_events(struct device_node *np,504				 struct exynos_ppmu *info)505{506	struct devfreq_event_desc *desc;507	struct device *dev = info->dev;508	struct device_node *events_np, *node;509	int i, j, count;510	int ret;511 512	events_np = of_get_child_by_name(np, "events");513	if (!events_np) {514		dev_err(dev,515			"failed to get child node of devfreq-event devices\n");516		return -EINVAL;517	}518 519	count = of_get_child_count(events_np);520	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);521	if (!desc) {522		of_node_put(events_np);523		return -ENOMEM;524	}525	info->num_events = count;526 527	info->ppmu_type = (enum exynos_ppmu_type)device_get_match_data(dev);528 529	j = 0;530	for_each_child_of_node(events_np, node) {531		for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {532			if (!ppmu_events[i].name)533				continue;534 535			if (of_node_name_eq(node, ppmu_events[i].name))536				break;537		}538 539		if (i == ARRAY_SIZE(ppmu_events)) {540			dev_warn(dev,541				"don't know how to configure events : %pOFn\n",542				node);543			continue;544		}545 546		switch (info->ppmu_type) {547		case EXYNOS_TYPE_PPMU:548			desc[j].ops = &exynos_ppmu_ops;549			break;550		case EXYNOS_TYPE_PPMU_V2:551			desc[j].ops = &exynos_ppmu_v2_ops;552			break;553		}554 555		desc[j].driver_data = info;556 557		of_property_read_string(node, "event-name", &desc[j].name);558		ret = of_property_read_u32(node, "event-data-type",559					   &desc[j].event_type);560		if (ret) {561			/* Set the event of proper data type counting.562			 * Check if the data type has been defined in DT,563			 * use default if not.564			 */565			if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {566				/* Not all registers take the same value for567				 * read+write data count.568				 */569				switch (ppmu_events[i].id) {570				case PPMU_PMNCNT0:571				case PPMU_PMNCNT1:572				case PPMU_PMNCNT2:573					desc[j].event_type = PPMU_V2_RO_DATA_CNT574						| PPMU_V2_WO_DATA_CNT;575					break;576				case PPMU_PMNCNT3:577					desc[j].event_type =578						PPMU_V2_EVT3_RW_DATA_CNT;579					break;580				}581			} else {582				desc[j].event_type = PPMU_RO_DATA_CNT |583					PPMU_WO_DATA_CNT;584			}585		}586 587		j++;588	}589	info->desc = desc;590 591	of_node_put(events_np);592 593	return 0;594}595 596static struct regmap_config exynos_ppmu_regmap_config = {597	.reg_bits = 32,598	.val_bits = 32,599	.reg_stride = 4,600};601 602static int exynos_ppmu_parse_dt(struct platform_device *pdev,603				struct exynos_ppmu *info)604{605	struct device *dev = info->dev;606	struct device_node *np = dev->of_node;607	struct resource *res;608	void __iomem *base;609	int ret = 0;610 611	if (!np) {612		dev_err(dev, "failed to find devicetree node\n");613		return -EINVAL;614	}615 616	/* Maps the memory mapped IO to control PPMU register */617	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);618	if (IS_ERR(base))619		return PTR_ERR(base);620 621	exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;622	info->regmap = devm_regmap_init_mmio(dev, base,623					&exynos_ppmu_regmap_config);624	if (IS_ERR(info->regmap)) {625		dev_err(dev, "failed to initialize regmap\n");626		return PTR_ERR(info->regmap);627	}628 629	info->ppmu.clk = devm_clk_get(dev, "ppmu");630	if (IS_ERR(info->ppmu.clk)) {631		info->ppmu.clk = NULL;632		dev_warn(dev, "cannot get PPMU clock\n");633	}634 635	ret = of_get_devfreq_events(np, info);636	if (ret < 0) {637		dev_err(dev, "failed to parse exynos ppmu dt node\n");638		return ret;639	}640 641	return 0;642}643 644static int exynos_ppmu_probe(struct platform_device *pdev)645{646	struct exynos_ppmu *info;647	struct devfreq_event_dev **edev;648	struct devfreq_event_desc *desc;649	int i, ret = 0, size;650 651	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);652	if (!info)653		return -ENOMEM;654 655	info->dev = &pdev->dev;656 657	/* Parse dt data to get resource */658	ret = exynos_ppmu_parse_dt(pdev, info);659	if (ret < 0) {660		dev_err(&pdev->dev,661			"failed to parse devicetree for resource\n");662		return ret;663	}664	desc = info->desc;665 666	size = sizeof(struct devfreq_event_dev *) * info->num_events;667	info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);668	if (!info->edev)669		return -ENOMEM;670 671	edev = info->edev;672	platform_set_drvdata(pdev, info);673 674	for (i = 0; i < info->num_events; i++) {675		edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);676		if (IS_ERR(edev[i])) {677			dev_err(&pdev->dev,678				"failed to add devfreq-event device\n");679			return PTR_ERR(edev[i]);680		}681 682		pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",683			dev_name(&pdev->dev), desc[i].name);684	}685 686	ret = clk_prepare_enable(info->ppmu.clk);687	if (ret) {688		dev_err(&pdev->dev, "failed to prepare ppmu clock\n");689		return ret;690	}691 692	return 0;693}694 695static void exynos_ppmu_remove(struct platform_device *pdev)696{697	struct exynos_ppmu *info = platform_get_drvdata(pdev);698 699	clk_disable_unprepare(info->ppmu.clk);700}701 702static struct platform_driver exynos_ppmu_driver = {703	.probe	= exynos_ppmu_probe,704	.remove_new = exynos_ppmu_remove,705	.driver = {706		.name	= "exynos-ppmu",707		.of_match_table = exynos_ppmu_id_match,708	},709};710module_platform_driver(exynos_ppmu_driver);711 712MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");713MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");714MODULE_LICENSE("GPL");715