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/cpu.h>
0008 #include <linux/cpufreq.h>
0009 #include <linux/err.h>
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/nvmem-consumer.h>
0014 #include <linux/of.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/pm_opp.h>
0017 #include <linux/regulator/consumer.h>
0018 #include <linux/slab.h>
0019 
0020 #include "cpufreq-dt.h"
0021 
0022 #define OCOTP_CFG3_SPEED_GRADE_SHIFT    8
0023 #define OCOTP_CFG3_SPEED_GRADE_MASK (0x3 << 8)
0024 #define IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK  (0xf << 8)
0025 #define OCOTP_CFG3_MKT_SEGMENT_SHIFT    6
0026 #define OCOTP_CFG3_MKT_SEGMENT_MASK     (0x3 << 6)
0027 #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT    5
0028 #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK     (0x3 << 5)
0029 
0030 #define IMX7ULP_MAX_RUN_FREQ    528000
0031 
0032 /* cpufreq-dt device registered by imx-cpufreq-dt */
0033 static struct platform_device *cpufreq_dt_pdev;
0034 static struct device *cpu_dev;
0035 static int cpufreq_opp_token;
0036 
0037 enum IMX7ULP_CPUFREQ_CLKS {
0038     ARM,
0039     CORE,
0040     SCS_SEL,
0041     HSRUN_CORE,
0042     HSRUN_SCS_SEL,
0043     FIRC,
0044 };
0045 
0046 static struct clk_bulk_data imx7ulp_clks[] = {
0047     { .id = "arm" },
0048     { .id = "core" },
0049     { .id = "scs_sel" },
0050     { .id = "hsrun_core" },
0051     { .id = "hsrun_scs_sel" },
0052     { .id = "firc" },
0053 };
0054 
0055 static unsigned int imx7ulp_get_intermediate(struct cpufreq_policy *policy,
0056                          unsigned int index)
0057 {
0058     return clk_get_rate(imx7ulp_clks[FIRC].clk);
0059 }
0060 
0061 static int imx7ulp_target_intermediate(struct cpufreq_policy *policy,
0062                     unsigned int index)
0063 {
0064     unsigned int newfreq = policy->freq_table[index].frequency;
0065 
0066     clk_set_parent(imx7ulp_clks[SCS_SEL].clk, imx7ulp_clks[FIRC].clk);
0067     clk_set_parent(imx7ulp_clks[HSRUN_SCS_SEL].clk, imx7ulp_clks[FIRC].clk);
0068 
0069     if (newfreq > IMX7ULP_MAX_RUN_FREQ)
0070         clk_set_parent(imx7ulp_clks[ARM].clk,
0071                    imx7ulp_clks[HSRUN_CORE].clk);
0072     else
0073         clk_set_parent(imx7ulp_clks[ARM].clk, imx7ulp_clks[CORE].clk);
0074 
0075     return 0;
0076 }
0077 
0078 static struct cpufreq_dt_platform_data imx7ulp_data = {
0079     .target_intermediate = imx7ulp_target_intermediate,
0080     .get_intermediate = imx7ulp_get_intermediate,
0081 };
0082 
0083 static int imx_cpufreq_dt_probe(struct platform_device *pdev)
0084 {
0085     struct platform_device *dt_pdev;
0086     u32 cell_value, supported_hw[2];
0087     int speed_grade, mkt_segment;
0088     int ret;
0089 
0090     cpu_dev = get_cpu_device(0);
0091 
0092     if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL))
0093         return -ENODEV;
0094 
0095     if (of_machine_is_compatible("fsl,imx7ulp")) {
0096         ret = clk_bulk_get(cpu_dev, ARRAY_SIZE(imx7ulp_clks),
0097                    imx7ulp_clks);
0098         if (ret)
0099             return ret;
0100 
0101         dt_pdev = platform_device_register_data(NULL, "cpufreq-dt",
0102                             -1, &imx7ulp_data,
0103                             sizeof(imx7ulp_data));
0104         if (IS_ERR(dt_pdev)) {
0105             clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
0106             ret = PTR_ERR(dt_pdev);
0107             dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
0108             return ret;
0109         }
0110 
0111         cpufreq_dt_pdev = dt_pdev;
0112 
0113         return 0;
0114     }
0115 
0116     ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value);
0117     if (ret)
0118         return ret;
0119 
0120     if (of_machine_is_compatible("fsl,imx8mn") ||
0121         of_machine_is_compatible("fsl,imx8mp"))
0122         speed_grade = (cell_value & IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK)
0123                   >> OCOTP_CFG3_SPEED_GRADE_SHIFT;
0124     else
0125         speed_grade = (cell_value & OCOTP_CFG3_SPEED_GRADE_MASK)
0126                   >> OCOTP_CFG3_SPEED_GRADE_SHIFT;
0127 
0128     if (of_machine_is_compatible("fsl,imx8mp"))
0129         mkt_segment = (cell_value & IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK)
0130                    >> IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT;
0131     else
0132         mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK)
0133                    >> OCOTP_CFG3_MKT_SEGMENT_SHIFT;
0134 
0135     /*
0136      * Early samples without fuses written report "0 0" which may NOT
0137      * match any OPP defined in DT. So clamp to minimum OPP defined in
0138      * DT to avoid warning for "no OPPs".
0139      *
0140      * Applies to i.MX8M series SoCs.
0141      */
0142     if (mkt_segment == 0 && speed_grade == 0) {
0143         if (of_machine_is_compatible("fsl,imx8mm") ||
0144             of_machine_is_compatible("fsl,imx8mq"))
0145             speed_grade = 1;
0146         if (of_machine_is_compatible("fsl,imx8mn") ||
0147             of_machine_is_compatible("fsl,imx8mp"))
0148             speed_grade = 0xb;
0149     }
0150 
0151     supported_hw[0] = BIT(speed_grade);
0152     supported_hw[1] = BIT(mkt_segment);
0153     dev_info(&pdev->dev, "cpu speed grade %d mkt segment %d supported-hw %#x %#x\n",
0154             speed_grade, mkt_segment, supported_hw[0], supported_hw[1]);
0155 
0156     cpufreq_opp_token = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2);
0157     if (cpufreq_opp_token < 0) {
0158         ret = cpufreq_opp_token;
0159         dev_err(&pdev->dev, "Failed to set supported opp: %d\n", ret);
0160         return ret;
0161     }
0162 
0163     cpufreq_dt_pdev = platform_device_register_data(
0164             &pdev->dev, "cpufreq-dt", -1, NULL, 0);
0165     if (IS_ERR(cpufreq_dt_pdev)) {
0166         dev_pm_opp_put_supported_hw(cpufreq_opp_token);
0167         ret = PTR_ERR(cpufreq_dt_pdev);
0168         dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
0169         return ret;
0170     }
0171 
0172     return 0;
0173 }
0174 
0175 static int imx_cpufreq_dt_remove(struct platform_device *pdev)
0176 {
0177     platform_device_unregister(cpufreq_dt_pdev);
0178     if (!of_machine_is_compatible("fsl,imx7ulp"))
0179         dev_pm_opp_put_supported_hw(cpufreq_opp_token);
0180     else
0181         clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
0182 
0183     return 0;
0184 }
0185 
0186 static struct platform_driver imx_cpufreq_dt_driver = {
0187     .probe = imx_cpufreq_dt_probe,
0188     .remove = imx_cpufreq_dt_remove,
0189     .driver = {
0190         .name = "imx-cpufreq-dt",
0191     },
0192 };
0193 module_platform_driver(imx_cpufreq_dt_driver);
0194 
0195 MODULE_ALIAS("platform:imx-cpufreq-dt");
0196 MODULE_DESCRIPTION("Freescale i.MX cpufreq speed grading driver");
0197 MODULE_LICENSE("GPL v2");