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 DRA7_EFUSE_HAS_ALL_MPU_OPP 23 29 30 #define DRA7_EFUSE_NOM_MPU_OPP BIT(0) 31 #define DRA7_EFUSE_OD_MPU_OPP BIT(1) 32 #define DRA7_EFUSE_HIGH_MPU_OPP BIT(2) 33 34 #define VERSION_COUNT 2 35 36 struct ti_cpufreq_data; 37 38 struct ti_cpufreq_soc_data { 39 unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data, 40 unsigned long efuse); 41 unsigned long efuse_fallback; 42 unsigned long efuse_offset; 43 unsigned long efuse_mask; 44 unsigned long efuse_shift; 45 unsigned long rev_offset; 46 bool multi_regulator; 47 }; 48 49 struct ti_cpufreq_data { 50 struct device *cpu_dev; 51 struct device_node *opp_node; 52 struct regmap *syscon; 53 const struct ti_cpufreq_soc_data *soc_data; 54 struct opp_table *opp_table; 55 }; 56 57 static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data, 58 unsigned long efuse) 59 { 60 if (!efuse) 61 efuse = opp_data->soc_data->efuse_fallback; 62 /* AM335x and AM437x use "OPP disable" bits, so invert */ 63 return ~efuse; 64 } 65 66 static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data, 67 unsigned long efuse) 68 { 69 unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP; 70 71 /* 72 * The efuse on dra7 and am57 parts contains a specific 73 * value indicating the highest available OPP. 74 */ 75 76 switch (efuse) { 77 case DRA7_EFUSE_HAS_ALL_MPU_OPP: 78 case DRA7_EFUSE_HAS_HIGH_MPU_OPP: 79 calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; 80 case DRA7_EFUSE_HAS_OD_MPU_OPP: 81 calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP; 82 } 83 84 return calculated_efuse; 85 } 86 87 static struct ti_cpufreq_soc_data am3x_soc_data = { 88 .efuse_xlate = amx3_efuse_xlate, 89 .efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ, 90 .efuse_offset = 0x07fc, 91 .efuse_mask = 0x1fff, 92 .rev_offset = 0x600, 93 .multi_regulator = false, 94 }; 95 96 static struct ti_cpufreq_soc_data am4x_soc_data = { 97 .efuse_xlate = amx3_efuse_xlate, 98 .efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ, 99 .efuse_offset = 0x0610, 100 .efuse_mask = 0x3f, 101 .rev_offset = 0x600, 102 .multi_regulator = false, 103 }; 104 105 static struct ti_cpufreq_soc_data dra7_soc_data = { 106 .efuse_xlate = dra7_efuse_xlate, 107 .efuse_offset = 0x020c, 108 .efuse_mask = 0xf80000, 109 .efuse_shift = 19, 110 .rev_offset = 0x204, 111 .multi_regulator = true, 112 }; 113 114 /** 115 * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC 116 * @opp_data: pointer to ti_cpufreq_data context 117 * @efuse_value: Set to the value parsed from efuse 118 * 119 * Returns error code if efuse not read properly. 120 */ 121 static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data, 122 u32 *efuse_value) 123 { 124 struct device *dev = opp_data->cpu_dev; 125 u32 efuse; 126 int ret; 127 128 ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset, 129 &efuse); 130 if (ret) { 131 dev_err(dev, 132 "Failed to read the efuse value from syscon: %d\n", 133 ret); 134 return ret; 135 } 136 137 efuse = (efuse & opp_data->soc_data->efuse_mask); 138 efuse >>= opp_data->soc_data->efuse_shift; 139 140 *efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse); 141 142 return 0; 143 } 144 145 /** 146 * ti_cpufreq_get_rev() - Parse and return rev value present on SoC 147 * @opp_data: pointer to ti_cpufreq_data context 148 * @revision_value: Set to the value parsed from revision register 149 * 150 * Returns error code if revision not read properly. 151 */ 152 static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data, 153 u32 *revision_value) 154 { 155 struct device *dev = opp_data->cpu_dev; 156 u32 revision; 157 int ret; 158 159 ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset, 160 &revision); 161 if (ret) { 162 dev_err(dev, 163 "Failed to read the revision number from syscon: %d\n", 164 ret); 165 return ret; 166 } 167 168 *revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK); 169 170 return 0; 171 } 172 173 static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data) 174 { 175 struct device *dev = opp_data->cpu_dev; 176 struct device_node *np = opp_data->opp_node; 177 178 opp_data->syscon = syscon_regmap_lookup_by_phandle(np, 179 "syscon"); 180 if (IS_ERR(opp_data->syscon)) { 181 dev_err(dev, 182 "\"syscon\" is missing, cannot use OPPv2 table.\n"); 183 return PTR_ERR(opp_data->syscon); 184 } 185 186 return 0; 187 } 188 189 static const struct of_device_id ti_cpufreq_of_match[] = { 190 { .compatible = "ti,am33xx", .data = &am3x_soc_data, }, 191 { .compatible = "ti,am43", .data = &am4x_soc_data, }, 192 { .compatible = "ti,dra7", .data = &dra7_soc_data }, 193 {}, 194 }; 195 196 static const struct of_device_id *ti_cpufreq_match_node(void) 197 { 198 struct device_node *np; 199 const struct of_device_id *match; 200 201 np = of_find_node_by_path("/"); 202 match = of_match_node(ti_cpufreq_of_match, np); 203 of_node_put(np); 204 205 return match; 206 } 207 208 static int ti_cpufreq_probe(struct platform_device *pdev) 209 { 210 u32 version[VERSION_COUNT]; 211 const struct of_device_id *match; 212 struct opp_table *ti_opp_table; 213 struct ti_cpufreq_data *opp_data; 214 const char * const reg_names[] = {"vdd", "vbb"}; 215 int ret; 216 217 match = dev_get_platdata(&pdev->dev); 218 if (!match) 219 return -ENODEV; 220 221 opp_data = devm_kzalloc(&pdev->dev, sizeof(*opp_data), GFP_KERNEL); 222 if (!opp_data) 223 return -ENOMEM; 224 225 opp_data->soc_data = match->data; 226 227 opp_data->cpu_dev = get_cpu_device(0); 228 if (!opp_data->cpu_dev) { 229 pr_err("%s: Failed to get device for CPU0\n", __func__); 230 return -ENODEV; 231 } 232 233 opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev); 234 if (!opp_data->opp_node) { 235 dev_info(opp_data->cpu_dev, 236 "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n"); 237 goto register_cpufreq_dt; 238 } 239 240 ret = ti_cpufreq_setup_syscon_register(opp_data); 241 if (ret) 242 goto fail_put_node; 243 244 /* 245 * OPPs determine whether or not they are supported based on 246 * two metrics: 247 * 0 - SoC Revision 248 * 1 - eFuse value 249 */ 250 ret = ti_cpufreq_get_rev(opp_data, &version[0]); 251 if (ret) 252 goto fail_put_node; 253 254 ret = ti_cpufreq_get_efuse(opp_data, &version[1]); 255 if (ret) 256 goto fail_put_node; 257 258 ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev, 259 version, VERSION_COUNT); 260 if (IS_ERR(ti_opp_table)) { 261 dev_err(opp_data->cpu_dev, 262 "Failed to set supported hardware\n"); 263 ret = PTR_ERR(ti_opp_table); 264 goto fail_put_node; 265 } 266 267 opp_data->opp_table = ti_opp_table; 268 269 if (opp_data->soc_data->multi_regulator) { 270 ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev, 271 reg_names, 272 ARRAY_SIZE(reg_names)); 273 if (IS_ERR(ti_opp_table)) { 274 dev_pm_opp_put_supported_hw(opp_data->opp_table); 275 ret = PTR_ERR(ti_opp_table); 276 goto fail_put_node; 277 } 278 } 279 280 of_node_put(opp_data->opp_node); 281 register_cpufreq_dt: 282 platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 283 284 return 0; 285 286 fail_put_node: 287 of_node_put(opp_data->opp_node); 288 289 return ret; 290 } 291 292 static int ti_cpufreq_init(void) 293 { 294 const struct of_device_id *match; 295 296 /* Check to ensure we are on a compatible platform */ 297 match = ti_cpufreq_match_node(); 298 if (match) 299 platform_device_register_data(NULL, "ti-cpufreq", -1, match, 300 sizeof(*match)); 301 302 return 0; 303 } 304 module_init(ti_cpufreq_init); 305 306 static struct platform_driver ti_cpufreq_driver = { 307 .probe = ti_cpufreq_probe, 308 .driver = { 309 .name = "ti-cpufreq", 310 }, 311 }; 312 builtin_platform_driver(ti_cpufreq_driver); 313 314 MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver"); 315 MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); 316 MODULE_LICENSE("GPL v2"); 317