.. | .. |
---|
| 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); |
---|