ti-cpufreq.c (11552B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * TI CPUFreq/OPP hw-supported driver 4 * 5 * Copyright (C) 2016-2017 Texas Instruments, Inc. 6 * Dave Gerlach <d-gerlach@ti.com> 7 */ 8 9#include <linux/cpu.h> 10#include <linux/io.h> 11#include <linux/mfd/syscon.h> 12#include <linux/module.h> 13#include <linux/init.h> 14#include <linux/of.h> 15#include <linux/of_platform.h> 16#include <linux/pm_opp.h> 17#include <linux/regmap.h> 18#include <linux/slab.h> 19 20#define REVISION_MASK 0xF 21#define REVISION_SHIFT 28 22 23#define AM33XX_800M_ARM_MPU_MAX_FREQ 0x1E2F 24#define AM43XX_600M_ARM_MPU_MAX_FREQ 0xFFA 25 26#define DRA7_EFUSE_HAS_OD_MPU_OPP 11 27#define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15 28#define DRA76_EFUSE_HAS_PLUS_MPU_OPP 18 29#define DRA7_EFUSE_HAS_ALL_MPU_OPP 23 30#define DRA76_EFUSE_HAS_ALL_MPU_OPP 24 31 32#define DRA7_EFUSE_NOM_MPU_OPP BIT(0) 33#define DRA7_EFUSE_OD_MPU_OPP BIT(1) 34#define DRA7_EFUSE_HIGH_MPU_OPP BIT(2) 35#define DRA76_EFUSE_PLUS_MPU_OPP BIT(3) 36 37#define OMAP3_CONTROL_DEVICE_STATUS 0x4800244C 38#define OMAP3_CONTROL_IDCODE 0x4830A204 39#define OMAP34xx_ProdID_SKUID 0x4830A20C 40#define OMAP3_SYSCON_BASE (0x48000000 + 0x2000 + 0x270) 41 42#define VERSION_COUNT 2 43 44struct ti_cpufreq_data; 45 46struct ti_cpufreq_soc_data { 47 const char * const *reg_names; 48 unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data, 49 unsigned long efuse); 50 unsigned long efuse_fallback; 51 unsigned long efuse_offset; 52 unsigned long efuse_mask; 53 unsigned long efuse_shift; 54 unsigned long rev_offset; 55 bool multi_regulator; 56}; 57 58struct ti_cpufreq_data { 59 struct device *cpu_dev; 60 struct device_node *opp_node; 61 struct regmap *syscon; 62 const struct ti_cpufreq_soc_data *soc_data; 63 struct opp_table *opp_table; 64}; 65 66static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data, 67 unsigned long efuse) 68{ 69 if (!efuse) 70 efuse = opp_data->soc_data->efuse_fallback; 71 /* AM335x and AM437x use "OPP disable" bits, so invert */ 72 return ~efuse; 73} 74 75static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data, 76 unsigned long efuse) 77{ 78 unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP; 79 80 /* 81 * The efuse on dra7 and am57 parts contains a specific 82 * value indicating the highest available OPP. 83 */ 84 85 switch (efuse) { 86 case DRA76_EFUSE_HAS_PLUS_MPU_OPP: 87 case DRA76_EFUSE_HAS_ALL_MPU_OPP: 88 calculated_efuse |= DRA76_EFUSE_PLUS_MPU_OPP; 89 fallthrough; 90 case DRA7_EFUSE_HAS_ALL_MPU_OPP: 91 case DRA7_EFUSE_HAS_HIGH_MPU_OPP: 92 calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; 93 fallthrough; 94 case DRA7_EFUSE_HAS_OD_MPU_OPP: 95 calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP; 96 } 97 98 return calculated_efuse; 99} 100 101static unsigned long omap3_efuse_xlate(struct ti_cpufreq_data *opp_data, 102 unsigned long efuse) 103{ 104 /* OPP enable bit ("Speed Binned") */ 105 return BIT(efuse); 106} 107 108static struct ti_cpufreq_soc_data am3x_soc_data = { 109 .efuse_xlate = amx3_efuse_xlate, 110 .efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ, 111 .efuse_offset = 0x07fc, 112 .efuse_mask = 0x1fff, 113 .rev_offset = 0x600, 114 .multi_regulator = false, 115}; 116 117static struct ti_cpufreq_soc_data am4x_soc_data = { 118 .efuse_xlate = amx3_efuse_xlate, 119 .efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ, 120 .efuse_offset = 0x0610, 121 .efuse_mask = 0x3f, 122 .rev_offset = 0x600, 123 .multi_regulator = false, 124}; 125 126static struct ti_cpufreq_soc_data dra7_soc_data = { 127 .efuse_xlate = dra7_efuse_xlate, 128 .efuse_offset = 0x020c, 129 .efuse_mask = 0xf80000, 130 .efuse_shift = 19, 131 .rev_offset = 0x204, 132 .multi_regulator = true, 133}; 134 135/* 136 * OMAP35x TRM (SPRUF98K): 137 * CONTROL_IDCODE (0x4830 A204) describes Silicon revisions. 138 * Control OMAP Status Register 15:0 (Address 0x4800 244C) 139 * to separate between omap3503, omap3515, omap3525, omap3530 140 * and feature presence. 141 * There are encodings for versions limited to 400/266MHz 142 * but we ignore. 143 * Not clear if this also holds for omap34xx. 144 * some eFuse values e.g. CONTROL_FUSE_OPP1_VDD1 145 * are stored in the SYSCON register range 146 * Register 0x4830A20C [ProdID.SKUID] [0:3] 147 * 0x0 for normal 600/430MHz device. 148 * 0x8 for 720/520MHz device. 149 * Not clear what omap34xx value is. 150 */ 151 152static struct ti_cpufreq_soc_data omap34xx_soc_data = { 153 .efuse_xlate = omap3_efuse_xlate, 154 .efuse_offset = OMAP34xx_ProdID_SKUID - OMAP3_SYSCON_BASE, 155 .efuse_shift = 3, 156 .efuse_mask = BIT(3), 157 .rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE, 158 .multi_regulator = false, 159}; 160 161/* 162 * AM/DM37x TRM (SPRUGN4M) 163 * CONTROL_IDCODE (0x4830 A204) describes Silicon revisions. 164 * Control Device Status Register 15:0 (Address 0x4800 244C) 165 * to separate between am3703, am3715, dm3725, dm3730 166 * and feature presence. 167 * Speed Binned = Bit 9 168 * 0 800/600 MHz 169 * 1 1000/800 MHz 170 * some eFuse values e.g. CONTROL_FUSE_OPP 1G_VDD1 171 * are stored in the SYSCON register range. 172 * There is no 0x4830A20C [ProdID.SKUID] register (exists but 173 * seems to always read as 0). 174 */ 175 176static const char * const omap3_reg_names[] = {"cpu0", "vbb"}; 177 178static struct ti_cpufreq_soc_data omap36xx_soc_data = { 179 .reg_names = omap3_reg_names, 180 .efuse_xlate = omap3_efuse_xlate, 181 .efuse_offset = OMAP3_CONTROL_DEVICE_STATUS - OMAP3_SYSCON_BASE, 182 .efuse_shift = 9, 183 .efuse_mask = BIT(9), 184 .rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE, 185 .multi_regulator = true, 186}; 187 188/* 189 * AM3517 is quite similar to AM/DM37x except that it has no 190 * high speed grade eFuse and no abb ldo 191 */ 192 193static struct ti_cpufreq_soc_data am3517_soc_data = { 194 .efuse_xlate = omap3_efuse_xlate, 195 .efuse_offset = OMAP3_CONTROL_DEVICE_STATUS - OMAP3_SYSCON_BASE, 196 .efuse_shift = 0, 197 .efuse_mask = 0, 198 .rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE, 199 .multi_regulator = false, 200}; 201 202 203/** 204 * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC 205 * @opp_data: pointer to ti_cpufreq_data context 206 * @efuse_value: Set to the value parsed from efuse 207 * 208 * Returns error code if efuse not read properly. 209 */ 210static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data, 211 u32 *efuse_value) 212{ 213 struct device *dev = opp_data->cpu_dev; 214 u32 efuse; 215 int ret; 216 217 ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset, 218 &efuse); 219 if (ret == -EIO) { 220 /* not a syscon register! */ 221 void __iomem *regs = ioremap(OMAP3_SYSCON_BASE + 222 opp_data->soc_data->efuse_offset, 4); 223 224 if (!regs) 225 return -ENOMEM; 226 efuse = readl(regs); 227 iounmap(regs); 228 } 229 else if (ret) { 230 dev_err(dev, 231 "Failed to read the efuse value from syscon: %d\n", 232 ret); 233 return ret; 234 } 235 236 efuse = (efuse & opp_data->soc_data->efuse_mask); 237 efuse >>= opp_data->soc_data->efuse_shift; 238 239 *efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse); 240 241 return 0; 242} 243 244/** 245 * ti_cpufreq_get_rev() - Parse and return rev value present on SoC 246 * @opp_data: pointer to ti_cpufreq_data context 247 * @revision_value: Set to the value parsed from revision register 248 * 249 * Returns error code if revision not read properly. 250 */ 251static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data, 252 u32 *revision_value) 253{ 254 struct device *dev = opp_data->cpu_dev; 255 u32 revision; 256 int ret; 257 258 ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset, 259 &revision); 260 if (ret == -EIO) { 261 /* not a syscon register! */ 262 void __iomem *regs = ioremap(OMAP3_SYSCON_BASE + 263 opp_data->soc_data->rev_offset, 4); 264 265 if (!regs) 266 return -ENOMEM; 267 revision = readl(regs); 268 iounmap(regs); 269 } 270 else if (ret) { 271 dev_err(dev, 272 "Failed to read the revision number from syscon: %d\n", 273 ret); 274 return ret; 275 } 276 277 *revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK); 278 279 return 0; 280} 281 282static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data) 283{ 284 struct device *dev = opp_data->cpu_dev; 285 struct device_node *np = opp_data->opp_node; 286 287 opp_data->syscon = syscon_regmap_lookup_by_phandle(np, 288 "syscon"); 289 if (IS_ERR(opp_data->syscon)) { 290 dev_err(dev, 291 "\"syscon\" is missing, cannot use OPPv2 table.\n"); 292 return PTR_ERR(opp_data->syscon); 293 } 294 295 return 0; 296} 297 298static const struct of_device_id ti_cpufreq_of_match[] = { 299 { .compatible = "ti,am33xx", .data = &am3x_soc_data, }, 300 { .compatible = "ti,am3517", .data = &am3517_soc_data, }, 301 { .compatible = "ti,am43", .data = &am4x_soc_data, }, 302 { .compatible = "ti,dra7", .data = &dra7_soc_data }, 303 { .compatible = "ti,omap34xx", .data = &omap34xx_soc_data, }, 304 { .compatible = "ti,omap36xx", .data = &omap36xx_soc_data, }, 305 /* legacy */ 306 { .compatible = "ti,omap3430", .data = &omap34xx_soc_data, }, 307 { .compatible = "ti,omap3630", .data = &omap36xx_soc_data, }, 308 {}, 309}; 310 311static const struct of_device_id *ti_cpufreq_match_node(void) 312{ 313 struct device_node *np; 314 const struct of_device_id *match; 315 316 np = of_find_node_by_path("/"); 317 match = of_match_node(ti_cpufreq_of_match, np); 318 of_node_put(np); 319 320 return match; 321} 322 323static int ti_cpufreq_probe(struct platform_device *pdev) 324{ 325 u32 version[VERSION_COUNT]; 326 const struct of_device_id *match; 327 struct opp_table *ti_opp_table; 328 struct ti_cpufreq_data *opp_data; 329 const char * const default_reg_names[] = {"vdd", "vbb"}; 330 int ret; 331 332 match = dev_get_platdata(&pdev->dev); 333 if (!match) 334 return -ENODEV; 335 336 opp_data = devm_kzalloc(&pdev->dev, sizeof(*opp_data), GFP_KERNEL); 337 if (!opp_data) 338 return -ENOMEM; 339 340 opp_data->soc_data = match->data; 341 342 opp_data->cpu_dev = get_cpu_device(0); 343 if (!opp_data->cpu_dev) { 344 pr_err("%s: Failed to get device for CPU0\n", __func__); 345 return -ENODEV; 346 } 347 348 opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev); 349 if (!opp_data->opp_node) { 350 dev_info(opp_data->cpu_dev, 351 "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n"); 352 goto register_cpufreq_dt; 353 } 354 355 ret = ti_cpufreq_setup_syscon_register(opp_data); 356 if (ret) 357 goto fail_put_node; 358 359 /* 360 * OPPs determine whether or not they are supported based on 361 * two metrics: 362 * 0 - SoC Revision 363 * 1 - eFuse value 364 */ 365 ret = ti_cpufreq_get_rev(opp_data, &version[0]); 366 if (ret) 367 goto fail_put_node; 368 369 ret = ti_cpufreq_get_efuse(opp_data, &version[1]); 370 if (ret) 371 goto fail_put_node; 372 373 ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev, 374 version, VERSION_COUNT); 375 if (IS_ERR(ti_opp_table)) { 376 dev_err(opp_data->cpu_dev, 377 "Failed to set supported hardware\n"); 378 ret = PTR_ERR(ti_opp_table); 379 goto fail_put_node; 380 } 381 382 opp_data->opp_table = ti_opp_table; 383 384 if (opp_data->soc_data->multi_regulator) { 385 const char * const *reg_names = default_reg_names; 386 387 if (opp_data->soc_data->reg_names) 388 reg_names = opp_data->soc_data->reg_names; 389 ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev, 390 reg_names, 391 ARRAY_SIZE(default_reg_names)); 392 if (IS_ERR(ti_opp_table)) { 393 dev_pm_opp_put_supported_hw(opp_data->opp_table); 394 ret = PTR_ERR(ti_opp_table); 395 goto fail_put_node; 396 } 397 } 398 399 of_node_put(opp_data->opp_node); 400register_cpufreq_dt: 401 platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 402 403 return 0; 404 405fail_put_node: 406 of_node_put(opp_data->opp_node); 407 408 return ret; 409} 410 411static int ti_cpufreq_init(void) 412{ 413 const struct of_device_id *match; 414 415 /* Check to ensure we are on a compatible platform */ 416 match = ti_cpufreq_match_node(); 417 if (match) 418 platform_device_register_data(NULL, "ti-cpufreq", -1, match, 419 sizeof(*match)); 420 421 return 0; 422} 423module_init(ti_cpufreq_init); 424 425static struct platform_driver ti_cpufreq_driver = { 426 .probe = ti_cpufreq_probe, 427 .driver = { 428 .name = "ti-cpufreq", 429 }, 430}; 431builtin_platform_driver(ti_cpufreq_driver); 432 433MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver"); 434MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); 435MODULE_LICENSE("GPL v2");