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