Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TI CPUFreq/OPP hw-supported driver
0004  *
0005  * Copyright (C) 2016-2017 Texas Instruments, Inc.
0006  *   Dave Gerlach <d-gerlach@ti.com>
0007  */
0008 
0009 #include <linux/cpu.h>
0010 #include <linux/io.h>
0011 #include <linux/mfd/syscon.h>
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 #include <linux/of.h>
0015 #include <linux/of_platform.h>
0016 #include <linux/pm_opp.h>
0017 #include <linux/regmap.h>
0018 #include <linux/slab.h>
0019 
0020 #define REVISION_MASK               0xF
0021 #define REVISION_SHIFT              28
0022 
0023 #define AM33XX_800M_ARM_MPU_MAX_FREQ        0x1E2F
0024 #define AM43XX_600M_ARM_MPU_MAX_FREQ        0xFFA
0025 
0026 #define DRA7_EFUSE_HAS_OD_MPU_OPP       11
0027 #define DRA7_EFUSE_HAS_HIGH_MPU_OPP     15
0028 #define DRA76_EFUSE_HAS_PLUS_MPU_OPP        18
0029 #define DRA7_EFUSE_HAS_ALL_MPU_OPP      23
0030 #define DRA76_EFUSE_HAS_ALL_MPU_OPP     24
0031 
0032 #define DRA7_EFUSE_NOM_MPU_OPP          BIT(0)
0033 #define DRA7_EFUSE_OD_MPU_OPP           BIT(1)
0034 #define DRA7_EFUSE_HIGH_MPU_OPP         BIT(2)
0035 #define DRA76_EFUSE_PLUS_MPU_OPP        BIT(3)
0036 
0037 #define OMAP3_CONTROL_DEVICE_STATUS     0x4800244C
0038 #define OMAP3_CONTROL_IDCODE            0x4830A204
0039 #define OMAP34xx_ProdID_SKUID           0x4830A20C
0040 #define OMAP3_SYSCON_BASE   (0x48000000 + 0x2000 + 0x270)
0041 
0042 #define VERSION_COUNT               2
0043 
0044 struct ti_cpufreq_data;
0045 
0046 struct ti_cpufreq_soc_data {
0047     const char * const *reg_names;
0048     unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data,
0049                      unsigned long efuse);
0050     unsigned long efuse_fallback;
0051     unsigned long efuse_offset;
0052     unsigned long efuse_mask;
0053     unsigned long efuse_shift;
0054     unsigned long rev_offset;
0055     bool multi_regulator;
0056 };
0057 
0058 struct ti_cpufreq_data {
0059     struct device *cpu_dev;
0060     struct device_node *opp_node;
0061     struct regmap *syscon;
0062     const struct ti_cpufreq_soc_data *soc_data;
0063 };
0064 
0065 static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
0066                       unsigned long efuse)
0067 {
0068     if (!efuse)
0069         efuse = opp_data->soc_data->efuse_fallback;
0070     /* AM335x and AM437x use "OPP disable" bits, so invert */
0071     return ~efuse;
0072 }
0073 
0074 static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data,
0075                       unsigned long efuse)
0076 {
0077     unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP;
0078 
0079     /*
0080      * The efuse on dra7 and am57 parts contains a specific
0081      * value indicating the highest available OPP.
0082      */
0083 
0084     switch (efuse) {
0085     case DRA76_EFUSE_HAS_PLUS_MPU_OPP:
0086     case DRA76_EFUSE_HAS_ALL_MPU_OPP:
0087         calculated_efuse |= DRA76_EFUSE_PLUS_MPU_OPP;
0088         fallthrough;
0089     case DRA7_EFUSE_HAS_ALL_MPU_OPP:
0090     case DRA7_EFUSE_HAS_HIGH_MPU_OPP:
0091         calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP;
0092         fallthrough;
0093     case DRA7_EFUSE_HAS_OD_MPU_OPP:
0094         calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP;
0095     }
0096 
0097     return calculated_efuse;
0098 }
0099 
0100 static unsigned long omap3_efuse_xlate(struct ti_cpufreq_data *opp_data,
0101                       unsigned long efuse)
0102 {
0103     /* OPP enable bit ("Speed Binned") */
0104     return BIT(efuse);
0105 }
0106 
0107 static struct ti_cpufreq_soc_data am3x_soc_data = {
0108     .efuse_xlate = amx3_efuse_xlate,
0109     .efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ,
0110     .efuse_offset = 0x07fc,
0111     .efuse_mask = 0x1fff,
0112     .rev_offset = 0x600,
0113     .multi_regulator = false,
0114 };
0115 
0116 static struct ti_cpufreq_soc_data am4x_soc_data = {
0117     .efuse_xlate = amx3_efuse_xlate,
0118     .efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ,
0119     .efuse_offset = 0x0610,
0120     .efuse_mask = 0x3f,
0121     .rev_offset = 0x600,
0122     .multi_regulator = false,
0123 };
0124 
0125 static struct ti_cpufreq_soc_data dra7_soc_data = {
0126     .efuse_xlate = dra7_efuse_xlate,
0127     .efuse_offset = 0x020c,
0128     .efuse_mask = 0xf80000,
0129     .efuse_shift = 19,
0130     .rev_offset = 0x204,
0131     .multi_regulator = true,
0132 };
0133 
0134 /*
0135  * OMAP35x TRM (SPRUF98K):
0136  *  CONTROL_IDCODE (0x4830 A204) describes Silicon revisions.
0137  *  Control OMAP Status Register 15:0 (Address 0x4800 244C)
0138  *    to separate between omap3503, omap3515, omap3525, omap3530
0139  *    and feature presence.
0140  *    There are encodings for versions limited to 400/266MHz
0141  *    but we ignore.
0142  *    Not clear if this also holds for omap34xx.
0143  *  some eFuse values e.g. CONTROL_FUSE_OPP1_VDD1
0144  *    are stored in the SYSCON register range
0145  *  Register 0x4830A20C [ProdID.SKUID] [0:3]
0146  *    0x0 for normal 600/430MHz device.
0147  *    0x8 for 720/520MHz device.
0148  *    Not clear what omap34xx value is.
0149  */
0150 
0151 static struct ti_cpufreq_soc_data omap34xx_soc_data = {
0152     .efuse_xlate = omap3_efuse_xlate,
0153     .efuse_offset = OMAP34xx_ProdID_SKUID - OMAP3_SYSCON_BASE,
0154     .efuse_shift = 3,
0155     .efuse_mask = BIT(3),
0156     .rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE,
0157     .multi_regulator = false,
0158 };
0159 
0160 /*
0161  * AM/DM37x TRM (SPRUGN4M)
0162  *  CONTROL_IDCODE (0x4830 A204) describes Silicon revisions.
0163  *  Control Device Status Register 15:0 (Address 0x4800 244C)
0164  *    to separate between am3703, am3715, dm3725, dm3730
0165  *    and feature presence.
0166  *   Speed Binned = Bit 9
0167  *     0 800/600 MHz
0168  *     1 1000/800 MHz
0169  *  some eFuse values e.g. CONTROL_FUSE_OPP 1G_VDD1
0170  *    are stored in the SYSCON register range.
0171  *  There is no 0x4830A20C [ProdID.SKUID] register (exists but
0172  *    seems to always read as 0).
0173  */
0174 
0175 static const char * const omap3_reg_names[] = {"cpu0", "vbb", NULL};
0176 
0177 static struct ti_cpufreq_soc_data omap36xx_soc_data = {
0178     .reg_names = omap3_reg_names,
0179     .efuse_xlate = omap3_efuse_xlate,
0180     .efuse_offset = OMAP3_CONTROL_DEVICE_STATUS - OMAP3_SYSCON_BASE,
0181     .efuse_shift = 9,
0182     .efuse_mask = BIT(9),
0183     .rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE,
0184     .multi_regulator = true,
0185 };
0186 
0187 /*
0188  * AM3517 is quite similar to AM/DM37x except that it has no
0189  * high speed grade eFuse and no abb ldo
0190  */
0191 
0192 static struct ti_cpufreq_soc_data am3517_soc_data = {
0193     .efuse_xlate = omap3_efuse_xlate,
0194     .efuse_offset = OMAP3_CONTROL_DEVICE_STATUS - OMAP3_SYSCON_BASE,
0195     .efuse_shift = 0,
0196     .efuse_mask = 0,
0197     .rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE,
0198     .multi_regulator = false,
0199 };
0200 
0201 
0202 /**
0203  * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
0204  * @opp_data: pointer to ti_cpufreq_data context
0205  * @efuse_value: Set to the value parsed from efuse
0206  *
0207  * Returns error code if efuse not read properly.
0208  */
0209 static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data,
0210                 u32 *efuse_value)
0211 {
0212     struct device *dev = opp_data->cpu_dev;
0213     u32 efuse;
0214     int ret;
0215 
0216     ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset,
0217               &efuse);
0218     if (ret == -EIO) {
0219         /* not a syscon register! */
0220         void __iomem *regs = ioremap(OMAP3_SYSCON_BASE +
0221                 opp_data->soc_data->efuse_offset, 4);
0222 
0223         if (!regs)
0224             return -ENOMEM;
0225         efuse = readl(regs);
0226         iounmap(regs);
0227         }
0228     else if (ret) {
0229         dev_err(dev,
0230             "Failed to read the efuse value from syscon: %d\n",
0231             ret);
0232         return ret;
0233     }
0234 
0235     efuse = (efuse & opp_data->soc_data->efuse_mask);
0236     efuse >>= opp_data->soc_data->efuse_shift;
0237 
0238     *efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse);
0239 
0240     return 0;
0241 }
0242 
0243 /**
0244  * ti_cpufreq_get_rev() - Parse and return rev value present on SoC
0245  * @opp_data: pointer to ti_cpufreq_data context
0246  * @revision_value: Set to the value parsed from revision register
0247  *
0248  * Returns error code if revision not read properly.
0249  */
0250 static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data,
0251                   u32 *revision_value)
0252 {
0253     struct device *dev = opp_data->cpu_dev;
0254     u32 revision;
0255     int ret;
0256 
0257     ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset,
0258               &revision);
0259     if (ret == -EIO) {
0260         /* not a syscon register! */
0261         void __iomem *regs = ioremap(OMAP3_SYSCON_BASE +
0262                 opp_data->soc_data->rev_offset, 4);
0263 
0264         if (!regs)
0265             return -ENOMEM;
0266         revision = readl(regs);
0267         iounmap(regs);
0268         }
0269     else if (ret) {
0270         dev_err(dev,
0271             "Failed to read the revision number from syscon: %d\n",
0272             ret);
0273         return ret;
0274     }
0275 
0276     *revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK);
0277 
0278     return 0;
0279 }
0280 
0281 static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data)
0282 {
0283     struct device *dev = opp_data->cpu_dev;
0284     struct device_node *np = opp_data->opp_node;
0285 
0286     opp_data->syscon = syscon_regmap_lookup_by_phandle(np,
0287                             "syscon");
0288     if (IS_ERR(opp_data->syscon)) {
0289         dev_err(dev,
0290             "\"syscon\" is missing, cannot use OPPv2 table.\n");
0291         return PTR_ERR(opp_data->syscon);
0292     }
0293 
0294     return 0;
0295 }
0296 
0297 static const struct of_device_id ti_cpufreq_of_match[] = {
0298     { .compatible = "ti,am33xx", .data = &am3x_soc_data, },
0299     { .compatible = "ti,am3517", .data = &am3517_soc_data, },
0300     { .compatible = "ti,am43", .data = &am4x_soc_data, },
0301     { .compatible = "ti,dra7", .data = &dra7_soc_data },
0302     { .compatible = "ti,omap34xx", .data = &omap34xx_soc_data, },
0303     { .compatible = "ti,omap36xx", .data = &omap36xx_soc_data, },
0304     /* legacy */
0305     { .compatible = "ti,omap3430", .data = &omap34xx_soc_data, },
0306     { .compatible = "ti,omap3630", .data = &omap36xx_soc_data, },
0307     {},
0308 };
0309 
0310 static const struct of_device_id *ti_cpufreq_match_node(void)
0311 {
0312     struct device_node *np;
0313     const struct of_device_id *match;
0314 
0315     np = of_find_node_by_path("/");
0316     match = of_match_node(ti_cpufreq_of_match, np);
0317     of_node_put(np);
0318 
0319     return match;
0320 }
0321 
0322 static int ti_cpufreq_probe(struct platform_device *pdev)
0323 {
0324     u32 version[VERSION_COUNT];
0325     const struct of_device_id *match;
0326     struct ti_cpufreq_data *opp_data;
0327     const char * const default_reg_names[] = {"vdd", "vbb", NULL};
0328     int ret;
0329     struct dev_pm_opp_config config = {
0330         .supported_hw = version,
0331         .supported_hw_count = ARRAY_SIZE(version),
0332     };
0333 
0334     match = dev_get_platdata(&pdev->dev);
0335     if (!match)
0336         return -ENODEV;
0337 
0338     opp_data = devm_kzalloc(&pdev->dev, sizeof(*opp_data), GFP_KERNEL);
0339     if (!opp_data)
0340         return -ENOMEM;
0341 
0342     opp_data->soc_data = match->data;
0343 
0344     opp_data->cpu_dev = get_cpu_device(0);
0345     if (!opp_data->cpu_dev) {
0346         pr_err("%s: Failed to get device for CPU0\n", __func__);
0347         return -ENODEV;
0348     }
0349 
0350     opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev);
0351     if (!opp_data->opp_node) {
0352         dev_info(opp_data->cpu_dev,
0353              "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n");
0354         goto register_cpufreq_dt;
0355     }
0356 
0357     ret = ti_cpufreq_setup_syscon_register(opp_data);
0358     if (ret)
0359         goto fail_put_node;
0360 
0361     /*
0362      * OPPs determine whether or not they are supported based on
0363      * two metrics:
0364      *  0 - SoC Revision
0365      *  1 - eFuse value
0366      */
0367     ret = ti_cpufreq_get_rev(opp_data, &version[0]);
0368     if (ret)
0369         goto fail_put_node;
0370 
0371     ret = ti_cpufreq_get_efuse(opp_data, &version[1]);
0372     if (ret)
0373         goto fail_put_node;
0374 
0375     if (opp_data->soc_data->multi_regulator) {
0376         if (opp_data->soc_data->reg_names)
0377             config.regulator_names = opp_data->soc_data->reg_names;
0378         else
0379             config.regulator_names = default_reg_names;
0380     }
0381 
0382     ret = dev_pm_opp_set_config(opp_data->cpu_dev, &config);
0383     if (ret < 0) {
0384         dev_err(opp_data->cpu_dev, "Failed to set OPP config\n");
0385         goto fail_put_node;
0386     }
0387 
0388     of_node_put(opp_data->opp_node);
0389 
0390 register_cpufreq_dt:
0391     platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
0392 
0393     return 0;
0394 
0395 fail_put_node:
0396     of_node_put(opp_data->opp_node);
0397 
0398     return ret;
0399 }
0400 
0401 static int ti_cpufreq_init(void)
0402 {
0403     const struct of_device_id *match;
0404 
0405     /* Check to ensure we are on a compatible platform */
0406     match = ti_cpufreq_match_node();
0407     if (match)
0408         platform_device_register_data(NULL, "ti-cpufreq", -1, match,
0409                           sizeof(*match));
0410 
0411     return 0;
0412 }
0413 module_init(ti_cpufreq_init);
0414 
0415 static struct platform_driver ti_cpufreq_driver = {
0416     .probe = ti_cpufreq_probe,
0417     .driver = {
0418         .name = "ti-cpufreq",
0419     },
0420 };
0421 builtin_platform_driver(ti_cpufreq_driver);
0422 
0423 MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver");
0424 MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
0425 MODULE_LICENSE("GPL v2");