| .. | .. | 
|---|
|  | 1 | +// SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 1 | 2 | /* | 
|---|
| 2 | 3 | * TI CPUFreq/OPP hw-supported driver | 
|---|
| 3 | 4 | * | 
|---|
| 4 | 5 | * Copyright (C) 2016-2017 Texas Instruments, Inc. | 
|---|
| 5 | 6 | *	 Dave Gerlach <d-gerlach@ti.com> | 
|---|
| 6 |  | - * | 
|---|
| 7 |  | - * This program is free software; you can redistribute it and/or | 
|---|
| 8 |  | - * modify it under the terms of the GNU General Public License | 
|---|
| 9 |  | - * version 2 as published by the Free Software Foundation. | 
|---|
| 10 |  | - * | 
|---|
| 11 |  | - * This program is distributed in the hope that it will be useful, | 
|---|
| 12 |  | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 13 |  | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 14 |  | - * GNU General Public License for more details. | 
|---|
| 15 | 7 | */ | 
|---|
| 16 | 8 |  | 
|---|
| 17 | 9 | #include <linux/cpu.h> | 
|---|
| .. | .. | 
|---|
| 33 | 25 |  | 
|---|
| 34 | 26 | #define DRA7_EFUSE_HAS_OD_MPU_OPP		11 | 
|---|
| 35 | 27 | #define DRA7_EFUSE_HAS_HIGH_MPU_OPP		15 | 
|---|
|  | 28 | +#define DRA76_EFUSE_HAS_PLUS_MPU_OPP		18 | 
|---|
| 36 | 29 | #define DRA7_EFUSE_HAS_ALL_MPU_OPP		23 | 
|---|
|  | 30 | +#define DRA76_EFUSE_HAS_ALL_MPU_OPP		24 | 
|---|
| 37 | 31 |  | 
|---|
| 38 | 32 | #define DRA7_EFUSE_NOM_MPU_OPP			BIT(0) | 
|---|
| 39 | 33 | #define DRA7_EFUSE_OD_MPU_OPP			BIT(1) | 
|---|
| 40 | 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 | 41 |  | 
|---|
| 42 | 42 | #define VERSION_COUNT				2 | 
|---|
| 43 | 43 |  | 
|---|
| 44 | 44 | struct ti_cpufreq_data; | 
|---|
| 45 | 45 |  | 
|---|
| 46 | 46 | struct ti_cpufreq_soc_data { | 
|---|
|  | 47 | +	const char * const *reg_names; | 
|---|
| 47 | 48 | unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data, | 
|---|
| 48 | 49 | unsigned long efuse); | 
|---|
| 49 | 50 | unsigned long efuse_fallback; | 
|---|
| .. | .. | 
|---|
| 82 | 83 | */ | 
|---|
| 83 | 84 |  | 
|---|
| 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; | 
|---|
| 85 | 90 | case DRA7_EFUSE_HAS_ALL_MPU_OPP: | 
|---|
| 86 | 91 | case DRA7_EFUSE_HAS_HIGH_MPU_OPP: | 
|---|
| 87 | 92 | calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; | 
|---|
|  | 93 | +		fallthrough; | 
|---|
| 88 | 94 | case DRA7_EFUSE_HAS_OD_MPU_OPP: | 
|---|
| 89 | 95 | calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP; | 
|---|
| 90 | 96 | } | 
|---|
| 91 | 97 |  | 
|---|
| 92 | 98 | return calculated_efuse; | 
|---|
|  | 99 | +} | 
|---|
|  | 100 | + | 
|---|
|  | 101 | +static 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); | 
|---|
| 93 | 106 | } | 
|---|
| 94 | 107 |  | 
|---|
| 95 | 108 | static struct ti_cpufreq_soc_data am3x_soc_data = { | 
|---|
| .. | .. | 
|---|
| 119 | 132 | .multi_regulator = true, | 
|---|
| 120 | 133 | }; | 
|---|
| 121 | 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 | + | 
|---|
|  | 152 | +static 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 | + | 
|---|
|  | 176 | +static const char * const omap3_reg_names[] = {"cpu0", "vbb"}; | 
|---|
|  | 177 | + | 
|---|
|  | 178 | +static 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 | + | 
|---|
|  | 193 | +static 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 | + | 
|---|
| 122 | 203 | /** | 
|---|
| 123 | 204 | * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC | 
|---|
| 124 | 205 | * @opp_data: pointer to ti_cpufreq_data context | 
|---|
| .. | .. | 
|---|
| 135 | 216 |  | 
|---|
| 136 | 217 | ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset, | 
|---|
| 137 | 218 | &efuse); | 
|---|
| 138 |  | -	if (ret) { | 
|---|
|  | 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) { | 
|---|
| 139 | 230 | dev_err(dev, | 
|---|
| 140 | 231 | "Failed to read the efuse value from syscon: %d\n", | 
|---|
| 141 | 232 | ret); | 
|---|
| .. | .. | 
|---|
| 166 | 257 |  | 
|---|
| 167 | 258 | ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset, | 
|---|
| 168 | 259 | &revision); | 
|---|
| 169 |  | -	if (ret) { | 
|---|
|  | 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) { | 
|---|
| 170 | 271 | dev_err(dev, | 
|---|
| 171 | 272 | "Failed to read the revision number from syscon: %d\n", | 
|---|
| 172 | 273 | ret); | 
|---|
| .. | .. | 
|---|
| 196 | 297 |  | 
|---|
| 197 | 298 | static const struct of_device_id ti_cpufreq_of_match[] = { | 
|---|
| 198 | 299 | { .compatible = "ti,am33xx", .data = &am3x_soc_data, }, | 
|---|
|  | 300 | +	{ .compatible = "ti,am3517", .data = &am3517_soc_data, }, | 
|---|
| 199 | 301 | { .compatible = "ti,am43", .data = &am4x_soc_data, }, | 
|---|
| 200 | 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, }, | 
|---|
| 201 | 308 | {}, | 
|---|
| 202 | 309 | }; | 
|---|
| 203 | 310 |  | 
|---|
| .. | .. | 
|---|
| 219 | 326 | const struct of_device_id *match; | 
|---|
| 220 | 327 | struct opp_table *ti_opp_table; | 
|---|
| 221 | 328 | struct ti_cpufreq_data *opp_data; | 
|---|
| 222 |  | -	const char * const reg_names[] = {"vdd", "vbb"}; | 
|---|
|  | 329 | +	const char * const default_reg_names[] = {"vdd", "vbb"}; | 
|---|
| 223 | 330 | int ret; | 
|---|
| 224 | 331 |  | 
|---|
| 225 | 332 | match = dev_get_platdata(&pdev->dev); | 
|---|
| .. | .. | 
|---|
| 275 | 382 | opp_data->opp_table = ti_opp_table; | 
|---|
| 276 | 383 |  | 
|---|
| 277 | 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; | 
|---|
| 278 | 389 | ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev, | 
|---|
| 279 | 390 | reg_names, | 
|---|
| 280 |  | -							 ARRAY_SIZE(reg_names)); | 
|---|
|  | 391 | +							 ARRAY_SIZE(default_reg_names)); | 
|---|
| 281 | 392 | if (IS_ERR(ti_opp_table)) { | 
|---|
| 282 | 393 | dev_pm_opp_put_supported_hw(opp_data->opp_table); | 
|---|
| 283 | 394 | ret =  PTR_ERR(ti_opp_table); | 
|---|