Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2019 NXP
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/devfreq.h>
0008 #include <linux/device.h>
0009 #include <linux/module.h>
0010 #include <linux/of_device.h>
0011 #include <linux/pm_opp.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/slab.h>
0014 
0015 struct imx_bus {
0016     struct devfreq_dev_profile profile;
0017     struct devfreq *devfreq;
0018     struct clk *clk;
0019     struct platform_device *icc_pdev;
0020 };
0021 
0022 static int imx_bus_target(struct device *dev,
0023         unsigned long *freq, u32 flags)
0024 {
0025     struct dev_pm_opp *new_opp;
0026     int ret;
0027 
0028     new_opp = devfreq_recommended_opp(dev, freq, flags);
0029     if (IS_ERR(new_opp)) {
0030         ret = PTR_ERR(new_opp);
0031         dev_err(dev, "failed to get recommended opp: %d\n", ret);
0032         return ret;
0033     }
0034     dev_pm_opp_put(new_opp);
0035 
0036     return dev_pm_opp_set_rate(dev, *freq);
0037 }
0038 
0039 static int imx_bus_get_cur_freq(struct device *dev, unsigned long *freq)
0040 {
0041     struct imx_bus *priv = dev_get_drvdata(dev);
0042 
0043     *freq = clk_get_rate(priv->clk);
0044 
0045     return 0;
0046 }
0047 
0048 static void imx_bus_exit(struct device *dev)
0049 {
0050     struct imx_bus *priv = dev_get_drvdata(dev);
0051 
0052     dev_pm_opp_of_remove_table(dev);
0053     platform_device_unregister(priv->icc_pdev);
0054 }
0055 
0056 /* imx_bus_init_icc() - register matching icc provider if required */
0057 static int imx_bus_init_icc(struct device *dev)
0058 {
0059     struct imx_bus *priv = dev_get_drvdata(dev);
0060     const char *icc_driver_name;
0061 
0062     if (!of_get_property(dev->of_node, "#interconnect-cells", NULL))
0063         return 0;
0064     if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
0065         dev_warn(dev, "imx interconnect drivers disabled\n");
0066         return 0;
0067     }
0068 
0069     icc_driver_name = of_device_get_match_data(dev);
0070     if (!icc_driver_name) {
0071         dev_err(dev, "unknown interconnect driver\n");
0072         return 0;
0073     }
0074 
0075     priv->icc_pdev = platform_device_register_data(
0076             dev, icc_driver_name, -1, NULL, 0);
0077     if (IS_ERR(priv->icc_pdev)) {
0078         dev_err(dev, "failed to register icc provider %s: %ld\n",
0079                 icc_driver_name, PTR_ERR(priv->icc_pdev));
0080         return PTR_ERR(priv->icc_pdev);
0081     }
0082 
0083     return 0;
0084 }
0085 
0086 static int imx_bus_probe(struct platform_device *pdev)
0087 {
0088     struct device *dev = &pdev->dev;
0089     struct imx_bus *priv;
0090     const char *gov = DEVFREQ_GOV_USERSPACE;
0091     int ret;
0092 
0093     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0094     if (!priv)
0095         return -ENOMEM;
0096 
0097     /*
0098      * Fetch the clock to adjust but don't explicitly enable.
0099      *
0100      * For imx bus clock clk_set_rate is safe no matter if the clock is on
0101      * or off and some peripheral side-buses might be off unless enabled by
0102      * drivers for devices on those specific buses.
0103      *
0104      * Rate adjustment on a disabled bus clock just takes effect later.
0105      */
0106     priv->clk = devm_clk_get(dev, NULL);
0107     if (IS_ERR(priv->clk)) {
0108         ret = PTR_ERR(priv->clk);
0109         dev_err(dev, "failed to fetch clk: %d\n", ret);
0110         return ret;
0111     }
0112     platform_set_drvdata(pdev, priv);
0113 
0114     ret = dev_pm_opp_of_add_table(dev);
0115     if (ret < 0) {
0116         dev_err(dev, "failed to get OPP table\n");
0117         return ret;
0118     }
0119 
0120     priv->profile.target = imx_bus_target;
0121     priv->profile.exit = imx_bus_exit;
0122     priv->profile.get_cur_freq = imx_bus_get_cur_freq;
0123     priv->profile.initial_freq = clk_get_rate(priv->clk);
0124 
0125     priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
0126                         gov, NULL);
0127     if (IS_ERR(priv->devfreq)) {
0128         ret = PTR_ERR(priv->devfreq);
0129         dev_err(dev, "failed to add devfreq device: %d\n", ret);
0130         goto err;
0131     }
0132 
0133     ret = imx_bus_init_icc(dev);
0134     if (ret)
0135         goto err;
0136 
0137     return 0;
0138 
0139 err:
0140     dev_pm_opp_of_remove_table(dev);
0141     return ret;
0142 }
0143 
0144 static const struct of_device_id imx_bus_of_match[] = {
0145     { .compatible = "fsl,imx8mq-noc", .data = "imx8mq-interconnect", },
0146     { .compatible = "fsl,imx8mm-noc", .data = "imx8mm-interconnect", },
0147     { .compatible = "fsl,imx8mn-noc", .data = "imx8mn-interconnect", },
0148     { .compatible = "fsl,imx8mp-noc", .data = "imx8mp-interconnect", },
0149     { .compatible = "fsl,imx8m-noc", },
0150     { .compatible = "fsl,imx8m-nic", },
0151     { /* sentinel */ },
0152 };
0153 MODULE_DEVICE_TABLE(of, imx_bus_of_match);
0154 
0155 static struct platform_driver imx_bus_platdrv = {
0156     .probe      = imx_bus_probe,
0157     .driver = {
0158         .name   = "imx-bus-devfreq",
0159         .of_match_table = imx_bus_of_match,
0160     },
0161 };
0162 module_platform_driver(imx_bus_platdrv);
0163 
0164 MODULE_DESCRIPTION("Generic i.MX bus frequency scaling driver");
0165 MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
0166 MODULE_LICENSE("GPL v2");