Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * exynos-nocp.c - Exynos NoC (Network On Chip) Probe support
0004  *
0005  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
0006  * Author : Chanwoo Choi <cw00.choi@samsung.com>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/module.h>
0011 #include <linux/devfreq-event.h>
0012 #include <linux/kernel.h>
0013 #include <linux/of_address.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/regmap.h>
0016 
0017 #include "exynos-nocp.h"
0018 
0019 struct exynos_nocp {
0020     struct devfreq_event_dev *edev;
0021     struct devfreq_event_desc desc;
0022 
0023     struct device *dev;
0024 
0025     struct regmap *regmap;
0026     struct clk *clk;
0027 };
0028 
0029 /*
0030  * The devfreq-event ops structure for nocp probe.
0031  */
0032 static int exynos_nocp_set_event(struct devfreq_event_dev *edev)
0033 {
0034     struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
0035     int ret;
0036 
0037     /* Disable NoC probe */
0038     ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
0039                 NOCP_MAIN_CTL_STATEN_MASK, 0);
0040     if (ret < 0) {
0041         dev_err(nocp->dev, "failed to disable the NoC probe device\n");
0042         return ret;
0043     }
0044 
0045     /* Set a statistics dump period to 0 */
0046     ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0);
0047     if (ret < 0)
0048         goto out;
0049 
0050     /* Set the IntEvent fields of *_SRC */
0051     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC,
0052                 NOCP_CNT_SRC_INTEVENT_MASK,
0053                 NOCP_CNT_SRC_INTEVENT_BYTE_MASK);
0054     if (ret < 0)
0055         goto out;
0056 
0057     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC,
0058                 NOCP_CNT_SRC_INTEVENT_MASK,
0059                 NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
0060     if (ret < 0)
0061         goto out;
0062 
0063     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC,
0064                 NOCP_CNT_SRC_INTEVENT_MASK,
0065                 NOCP_CNT_SRC_INTEVENT_CYCLE_MASK);
0066     if (ret < 0)
0067         goto out;
0068 
0069     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC,
0070                 NOCP_CNT_SRC_INTEVENT_MASK,
0071                 NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
0072     if (ret < 0)
0073         goto out;
0074 
0075 
0076     /* Set an alarm with a max/min value of 0 to generate StatALARM */
0077     ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0);
0078     if (ret < 0)
0079         goto out;
0080 
0081     ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0);
0082     if (ret < 0)
0083         goto out;
0084 
0085     /* Set AlarmMode */
0086     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE,
0087                 NOCP_CNT_ALARM_MODE_MASK,
0088                 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
0089     if (ret < 0)
0090         goto out;
0091 
0092     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE,
0093                 NOCP_CNT_ALARM_MODE_MASK,
0094                 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
0095     if (ret < 0)
0096         goto out;
0097 
0098     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE,
0099                 NOCP_CNT_ALARM_MODE_MASK,
0100                 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
0101     if (ret < 0)
0102         goto out;
0103 
0104     ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE,
0105                 NOCP_CNT_ALARM_MODE_MASK,
0106                 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
0107     if (ret < 0)
0108         goto out;
0109 
0110     /* Enable the measurements by setting AlarmEn and StatEn */
0111     ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
0112             NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK,
0113             NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK);
0114     if (ret < 0)
0115         goto out;
0116 
0117     /* Set GlobalEN */
0118     ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL,
0119                 NOCP_CFG_CTL_GLOBALEN_MASK,
0120                 NOCP_CFG_CTL_GLOBALEN_MASK);
0121     if (ret < 0)
0122         goto out;
0123 
0124     /* Enable NoC probe */
0125     ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
0126                 NOCP_MAIN_CTL_STATEN_MASK,
0127                 NOCP_MAIN_CTL_STATEN_MASK);
0128     if (ret < 0)
0129         goto out;
0130 
0131     return 0;
0132 
0133 out:
0134     /* Reset NoC probe */
0135     if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
0136                 NOCP_MAIN_CTL_STATEN_MASK, 0)) {
0137         dev_err(nocp->dev, "Failed to reset NoC probe device\n");
0138     }
0139 
0140     return ret;
0141 }
0142 
0143 static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
0144                 struct devfreq_event_data *edata)
0145 {
0146     struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
0147     unsigned int counter[4];
0148     int ret;
0149 
0150     /* Read cycle count */
0151     ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]);
0152     if (ret < 0)
0153         goto out;
0154 
0155     ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]);
0156     if (ret < 0)
0157         goto out;
0158 
0159     ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]);
0160     if (ret < 0)
0161         goto out;
0162 
0163     ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]);
0164     if (ret < 0)
0165         goto out;
0166 
0167     edata->load_count = ((counter[1] << 16) | counter[0]);
0168     edata->total_count = ((counter[3] << 16) | counter[2]);
0169 
0170     dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
0171                     edata->load_count, edata->total_count);
0172 
0173     return 0;
0174 
0175 out:
0176     dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
0177 
0178     return ret;
0179 }
0180 
0181 static const struct devfreq_event_ops exynos_nocp_ops = {
0182     .set_event = exynos_nocp_set_event,
0183     .get_event = exynos_nocp_get_event,
0184 };
0185 
0186 static const struct of_device_id exynos_nocp_id_match[] = {
0187     { .compatible = "samsung,exynos5420-nocp", },
0188     { /* sentinel */ },
0189 };
0190 MODULE_DEVICE_TABLE(of, exynos_nocp_id_match);
0191 
0192 static struct regmap_config exynos_nocp_regmap_config = {
0193     .reg_bits = 32,
0194     .val_bits = 32,
0195     .reg_stride = 4,
0196     .max_register = NOCP_COUNTERS_3_VAL,
0197 };
0198 
0199 static int exynos_nocp_parse_dt(struct platform_device *pdev,
0200                 struct exynos_nocp *nocp)
0201 {
0202     struct device *dev = nocp->dev;
0203     struct device_node *np = dev->of_node;
0204     struct resource *res;
0205     void __iomem *base;
0206 
0207     if (!np) {
0208         dev_err(dev, "failed to find devicetree node\n");
0209         return -EINVAL;
0210     }
0211 
0212     nocp->clk = devm_clk_get(dev, "nocp");
0213     if (IS_ERR(nocp->clk))
0214         nocp->clk = NULL;
0215 
0216     /* Maps the memory mapped IO to control nocp register */
0217     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0218     base = devm_ioremap_resource(dev, res);
0219     if (IS_ERR(base))
0220         return PTR_ERR(base);
0221 
0222     exynos_nocp_regmap_config.max_register = resource_size(res) - 4;
0223 
0224     nocp->regmap = devm_regmap_init_mmio(dev, base,
0225                     &exynos_nocp_regmap_config);
0226     if (IS_ERR(nocp->regmap)) {
0227         dev_err(dev, "failed to initialize regmap\n");
0228         return PTR_ERR(nocp->regmap);
0229     }
0230 
0231     return 0;
0232 }
0233 
0234 static int exynos_nocp_probe(struct platform_device *pdev)
0235 {
0236     struct device *dev = &pdev->dev;
0237     struct device_node *np = dev->of_node;
0238     struct exynos_nocp *nocp;
0239     int ret;
0240 
0241     nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL);
0242     if (!nocp)
0243         return -ENOMEM;
0244 
0245     nocp->dev = &pdev->dev;
0246 
0247     /* Parse dt data to get resource */
0248     ret = exynos_nocp_parse_dt(pdev, nocp);
0249     if (ret < 0) {
0250         dev_err(&pdev->dev,
0251             "failed to parse devicetree for resource\n");
0252         return ret;
0253     }
0254 
0255     /* Add devfreq-event device to measure the bandwidth of NoC */
0256     nocp->desc.ops = &exynos_nocp_ops;
0257     nocp->desc.driver_data = nocp;
0258     nocp->desc.name = np->full_name;
0259     nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc);
0260     if (IS_ERR(nocp->edev)) {
0261         dev_err(&pdev->dev,
0262             "failed to add devfreq-event device\n");
0263         return PTR_ERR(nocp->edev);
0264     }
0265     platform_set_drvdata(pdev, nocp);
0266 
0267     ret = clk_prepare_enable(nocp->clk);
0268     if (ret) {
0269         dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
0270         return ret;
0271     }
0272 
0273     pr_info("exynos-nocp: new NoC Probe device registered: %s\n",
0274             dev_name(dev));
0275 
0276     return 0;
0277 }
0278 
0279 static int exynos_nocp_remove(struct platform_device *pdev)
0280 {
0281     struct exynos_nocp *nocp = platform_get_drvdata(pdev);
0282 
0283     clk_disable_unprepare(nocp->clk);
0284 
0285     return 0;
0286 }
0287 
0288 static struct platform_driver exynos_nocp_driver = {
0289     .probe  = exynos_nocp_probe,
0290     .remove = exynos_nocp_remove,
0291     .driver = {
0292         .name   = "exynos-nocp",
0293         .of_match_table = exynos_nocp_id_match,
0294     },
0295 };
0296 module_platform_driver(exynos_nocp_driver);
0297 
0298 MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver");
0299 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
0300 MODULE_LICENSE("GPL");