/* * The devfreq-event ops structure for PPMU v1.1
*/ staticint exynos_ppmu_disable(struct devfreq_event_dev *edev)
{ struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); int ret;
u32 pmnc;
/* Disable all counters */
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 */
ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc); if (ret < 0) return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_PMNC, pmnc); if (ret < 0) return ret;
return 0;
}
staticint 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 */
ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens); if (ret < 0) return ret;
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
ret = regmap_write(info->regmap, PPMU_CNTENS, cntens); if (ret < 0) return ret;
/* Set the event of proper data type monitoring */
ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
edev->desc->event_type); if (ret < 0) return ret;
/* Reset cycle counter/performance counter and enable PPMU */
ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc); if (ret < 0) return ret;
/* * The devfreq-event ops structure for PPMU v2.0
*/ staticint 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;
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 */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc); if (ret < 0) return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc); if (ret < 0) return ret;
return 0;
}
staticint exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
{ struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); unsignedint pmnc, cntens; int id = exynos_ppmu_find_ppmu_id(edev); int ret;
/* Enable all counters */
ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens); if (ret < 0) return ret;
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens); if (ret < 0) return ret;
/* Set the event of proper data type monitoring */
ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
edev->desc->event_type); if (ret < 0) return ret;
/* Reset cycle counter/performance counter and enable PPMU */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc); if (ret < 0) return ret;
/* Disable PPMU */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc); if (ret < 0) return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc); if (ret < 0) return ret;
/* Read cycle count and performance count */
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:
ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count); if (ret < 0) return ret;
load_count = count; break; case PPMU_PMNCNT3:
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;
staticint of_get_devfreq_events(struct device_node *np, struct exynos_ppmu *info)
{ struct devfreq_event_desc *desc; struct device *dev = info->dev; struct device_node *events_np, *node; int i, j, count; int ret;
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;
}
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_name_eq(node, ppmu_events[i].name)) break;
}
if (i == ARRAY_SIZE(ppmu_events)) {
dev_warn(dev, "don't know how to configure events : %pOFn\n",
node); continue;
}
switch (info->ppmu_type) { case EXYNOS_TYPE_PPMU:
desc[j].ops = &exynos_ppmu_ops; break; case EXYNOS_TYPE_PPMU_V2:
desc[j].ops = &exynos_ppmu_v2_ops; break;
}
desc[j].driver_data = info;
of_property_read_string(node, "event-name", &desc[j].name);
ret = of_property_read_u32(node, "event-data-type",
&desc[j].event_type); if (ret) { /* Set the event of proper data type counting. * Check if the data type has been defined in DT, * use default if not.
*/ if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) { /* Not all registers take the same value for * read+write data count.
*/ switch (ppmu_events[i].id) { case PPMU_PMNCNT0: case PPMU_PMNCNT1: case PPMU_PMNCNT2:
desc[j].event_type = PPMU_V2_RO_DATA_CNT
| PPMU_V2_WO_DATA_CNT; break; case PPMU_PMNCNT3:
desc[j].event_type =
PPMU_V2_EVT3_RW_DATA_CNT; break;
}
} else {
desc[j].event_type = PPMU_RO_DATA_CNT |
PPMU_WO_DATA_CNT;
}
}
if (!np) {
dev_err(dev, "failed to find devicetree node\n"); return -EINVAL;
}
/* Maps the memory mapped IO to control PPMU register */
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) return PTR_ERR(base);
info->ppmu.clk = devm_clk_get(dev, "ppmu"); if (IS_ERR(info->ppmu.clk)) {
info->ppmu.clk = NULL;
dev_warn(dev, "cannot get PPMU clock\n");
}
ret = of_get_devfreq_events(np, info); if (ret < 0) {
dev_err(dev, "failed to parse exynos ppmu dt node\n"); return ret;
}
return 0;
}
staticint exynos_ppmu_probe(struct platform_device *pdev)
{ struct exynos_ppmu *info; struct devfreq_event_dev **edev; struct devfreq_event_desc *desc; int i, ret = 0, size;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
info->dev = &pdev->dev;
/* Parse dt data to get resource */
ret = exynos_ppmu_parse_dt(pdev, info); if (ret < 0) {
dev_err(&pdev->dev, "failed to parse devicetree for resource\n"); return ret;
}
desc = info->desc;
for (i = 0; i < info->num_events; i++) {
edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); if (IS_ERR(edev[i])) {
dev_err(&pdev->dev, "failed to add devfreq-event device\n"); return PTR_ERR(edev[i]);
}
pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
dev_name(&pdev->dev), desc[i].name);
}
ret = clk_prepare_enable(info->ppmu.clk); if (ret) {
dev_err(&pdev->dev, "failed to prepare ppmu clock\n"); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.