blob: 725fc585cabe220aae950350ee100189cf1ebaf7 [file] [log] [blame]
David Collins7370f1a2017-01-18 16:21:53 -08001/*
Tirupathi Reddy526719c2018-03-08 13:39:16 +05302 * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
David Collins7370f1a2017-01-18 16:21:53 -08003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/bitops.h>
17#include <linux/debugfs.h>
18#include <linux/err.h>
19#include <linux/init.h>
20#include <linux/interrupt.h>
21#include <linux/io.h>
22#include <linux/kernel.h>
23#include <linux/list.h>
24#include <linux/module.h>
25#include <linux/of.h>
26#include <linux/of_device.h>
27#include <linux/platform_device.h>
28#include <linux/pm_opp.h>
29#include <linux/slab.h>
30#include <linux/string.h>
31#include <linux/uaccess.h>
32#include <linux/regulator/driver.h>
33#include <linux/regulator/machine.h>
34#include <linux/regulator/of_regulator.h>
35
36#include "cpr3-regulator.h"
37
38#define MSM8953_APSS_FUSE_CORNERS 4
Tirupathi Reddy037708a2018-04-18 14:53:24 +053039#define SDM632_POWER_APSS_FUSE_CORNERS 4
40#define SDM632_PERF_APSS_FUSE_CORNERS 4
David Collins7370f1a2017-01-18 16:21:53 -080041
42/**
Tirupathi Reddy526719c2018-03-08 13:39:16 +053043 * struct cpr4_apss_fuses - APSS specific fuse data
David Collins7370f1a2017-01-18 16:21:53 -080044 * @ro_sel: Ring oscillator select fuse parameter value for each
45 * fuse corner
46 * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value
47 * for each fuse corner (raw, not converted to a voltage)
48 * @target_quot: CPR target quotient fuse parameter value for each fuse
49 * corner
50 * @quot_offset: CPR target quotient offset fuse parameter value for each
51 * fuse corner (raw, not unpacked) used for target quotient
52 * interpolation
53 * @speed_bin: Application processor speed bin fuse parameter value for
54 * the given chip
55 * @cpr_fusing_rev: CPR fusing revision fuse parameter value
Tirupathi Reddy53d99a02016-08-08 17:04:23 +053056 * @foundry_id: Foundry identifier fuse parameter value for the given
57 * chip
David Collins7370f1a2017-01-18 16:21:53 -080058 * @boost_cfg: CPR boost configuration fuse parameter value
59 * @boost_voltage: CPR boost voltage fuse parameter value (raw, not
60 * converted to a voltage)
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +053061 * @aging_init_quot_diff: Initial quotient difference between CPR aging
62 * min and max sensors measured at time of manufacturing
David Collins7370f1a2017-01-18 16:21:53 -080063 *
64 * This struct holds the values for all of the fuses read from memory.
65 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +053066struct cpr4_apss_fuses {
67 u64 *ro_sel;
68 u64 *init_voltage;
69 u64 *target_quot;
70 u64 *quot_offset;
David Collins7370f1a2017-01-18 16:21:53 -080071 u64 speed_bin;
72 u64 cpr_fusing_rev;
Tirupathi Reddy53d99a02016-08-08 17:04:23 +053073 u64 foundry_id;
David Collins7370f1a2017-01-18 16:21:53 -080074 u64 boost_cfg;
75 u64 boost_voltage;
76 u64 misc;
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +053077 u64 aging_init_quot_diff;
David Collins7370f1a2017-01-18 16:21:53 -080078};
79
80/*
81 * fuse combo = fusing revision + 8 * (speed bin)
82 * where: fusing revision = 0 - 7 and speed bin = 0 - 7
83 */
84#define CPR4_MSM8953_APSS_FUSE_COMBO_COUNT 64
Tirupathi Reddy526719c2018-03-08 13:39:16 +053085#define CPR4_SDM632_APSS_FUSE_COMBO_COUNT 64
David Collins7370f1a2017-01-18 16:21:53 -080086
87/*
88 * Constants which define the name of each fuse corner.
89 */
90enum cpr4_msm8953_apss_fuse_corner {
91 CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS = 0,
92 CPR4_MSM8953_APSS_FUSE_CORNER_SVS = 1,
93 CPR4_MSM8953_APSS_FUSE_CORNER_NOM = 2,
94 CPR4_MSM8953_APSS_FUSE_CORNER_TURBO_L1 = 3,
95};
96
97static const char * const cpr4_msm8953_apss_fuse_corner_name[] = {
98 [CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS] = "LowSVS",
99 [CPR4_MSM8953_APSS_FUSE_CORNER_SVS] = "SVS",
100 [CPR4_MSM8953_APSS_FUSE_CORNER_NOM] = "NOM",
101 [CPR4_MSM8953_APSS_FUSE_CORNER_TURBO_L1] = "TURBO_L1",
102};
103
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530104enum cpr4_sdm632_power_apss_fuse_corner {
105 CPR4_SDM632_POWER_APSS_FUSE_CORNER_LOWSVS = 0,
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530106 CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS_L1 = 1,
107 CPR4_SDM632_POWER_APSS_FUSE_CORNER_NOM = 2,
108 CPR4_SDM632_POWER_APSS_FUSE_CORNER_TURBO_L1 = 3,
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530109};
110
111static const char * const cpr4_sdm632_power_apss_fuse_corner_name[] = {
112 [CPR4_SDM632_POWER_APSS_FUSE_CORNER_LOWSVS] = "LowSVS",
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530113 [CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS_L1] = "SVS_L1",
114 [CPR4_SDM632_POWER_APSS_FUSE_CORNER_NOM] = "NOM",
115 [CPR4_SDM632_POWER_APSS_FUSE_CORNER_TURBO_L1] = "TURBO_L1",
116};
117
118enum cpr4_sdm632_perf_apss_fuse_corner {
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530119 CPR4_SDM632_PERF_APSS_FUSE_CORNER_LOWSVS = 0,
120 CPR4_SDM632_PERF_APSS_FUSE_CORNER_SVS_L1 = 1,
121 CPR4_SDM632_PERF_APSS_FUSE_CORNER_NOM = 2,
122 CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1 = 3,
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530123};
124
125static const char * const cpr4_sdm632_perf_apss_fuse_corner_name[] = {
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530126 [CPR4_SDM632_PERF_APSS_FUSE_CORNER_LOWSVS] = "LowSVS",
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530127 [CPR4_SDM632_PERF_APSS_FUSE_CORNER_SVS_L1] = "SVS_L1",
128 [CPR4_SDM632_PERF_APSS_FUSE_CORNER_NOM] = "NOM",
129 [CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1] = "TURBO_L1",
130};
131
132/* APSS cluster thread IDs */
133#define CPR4_APSS_POWER_CLUSTER_ID 0
134#define CPR4_APSS_PERF_CLUSTER_ID 1
135
David Collins7370f1a2017-01-18 16:21:53 -0800136/*
137 * MSM8953 APSS fuse parameter locations:
138 *
139 * Structs are organized with the following dimensions:
140 * Outer: 0 to 3 for fuse corners from lowest to highest corner
141 * Inner: large enough to hold the longest set of parameter segments which
142 * fully defines a fuse parameter, +1 (for NULL termination).
143 * Each segment corresponds to a contiguous group of bits from a
144 * single fuse row. These segments are concatentated together in
145 * order to form the full fuse parameter value. The segments for
146 * a given parameter may correspond to different fuse rows.
147 */
148static const struct cpr3_fuse_param
149msm8953_apss_ro_sel_param[MSM8953_APSS_FUSE_CORNERS][2] = {
150 {{73, 12, 15}, {} },
151 {{73, 8, 11}, {} },
152 {{73, 4, 7}, {} },
153 {{73, 0, 3}, {} },
154};
155
156static const struct cpr3_fuse_param
157msm8953_apss_init_voltage_param[MSM8953_APSS_FUSE_CORNERS][2] = {
158 {{71, 24, 29}, {} },
159 {{71, 18, 23}, {} },
160 {{71, 12, 17}, {} },
161 {{71, 6, 11}, {} },
162};
163
164static const struct cpr3_fuse_param
165msm8953_apss_target_quot_param[MSM8953_APSS_FUSE_CORNERS][2] = {
166 {{72, 44, 55}, {} },
167 {{72, 32, 43}, {} },
168 {{72, 20, 31}, {} },
169 {{72, 8, 19}, {} },
170};
171
172static const struct cpr3_fuse_param
173msm8953_apss_quot_offset_param[MSM8953_APSS_FUSE_CORNERS][2] = {
174 {{} },
175 {{71, 46, 52}, {} },
176 {{71, 39, 45}, {} },
177 {{71, 32, 38}, {} },
178};
179
180static const struct cpr3_fuse_param msm8953_cpr_fusing_rev_param[] = {
181 {71, 53, 55},
182 {},
183};
184
185static const struct cpr3_fuse_param msm8953_apss_speed_bin_param[] = {
186 {36, 40, 42},
187 {},
188};
189
Tirupathi Reddy53d99a02016-08-08 17:04:23 +0530190static const struct cpr3_fuse_param msm8953_apss_foundry_id_param[] = {
191 {37, 40, 42},
192 {},
193};
194
David Collins7370f1a2017-01-18 16:21:53 -0800195static const struct cpr3_fuse_param msm8953_cpr_boost_fuse_cfg_param[] = {
196 {36, 43, 45},
197 {},
198};
199
200static const struct cpr3_fuse_param msm8953_apss_boost_fuse_volt_param[] = {
201 {71, 0, 5},
202 {},
203};
204
205static const struct cpr3_fuse_param msm8953_misc_fuse_volt_adj_param[] = {
206 {36, 54, 54},
207 {},
208};
209
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +0530210static const struct cpr3_fuse_param msm8953_apss_aging_init_quot_diff_param[]
211= {
212 {72, 0, 7},
213 {},
214};
215
David Collins7370f1a2017-01-18 16:21:53 -0800216/*
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530217 * SDM632 APSS fuse parameter locations:
218 *
219 * Structs are organized with the following dimensions:
220 * Outer: 0 to 3 for fuse corners from lowest to highest corner
221 * Inner: large enough to hold the longest set of parameter segments which
222 * fully defines a fuse parameter, +1 (for NULL termination).
223 * Each segment corresponds to a contiguous group of bits from a
224 * single fuse row. These segments are concatentated together in
225 * order to form the full fuse parameter value. The segments for
226 * a given parameter may correspond to different fuse rows.
227 */
228static const struct cpr3_fuse_param
229sdm632_apss_ro_sel_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
230 [CPR4_APSS_POWER_CLUSTER_ID] = {
231 {{73, 28, 31}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530232 {{73, 20, 23}, {} },
233 {{73, 16, 19}, {} },
234 {{73, 12, 15}, {} },
235 },
236 [CPR4_APSS_PERF_CLUSTER_ID] = {
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530237 {{73, 28, 31}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530238 {{73, 8, 11}, {} },
239 {{73, 4, 7}, {} },
240 {{73, 0, 3}, {} },
241 },
242};
243
244static const struct cpr3_fuse_param
245sdm632_apss_init_voltage_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
246 [CPR4_APSS_POWER_CLUSTER_ID] = {
247 {{74, 18, 23}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530248 {{71, 24, 29}, {} },
249 {{74, 6, 11}, {} },
250 {{74, 0, 5}, {} },
251 },
252 [CPR4_APSS_PERF_CLUSTER_ID] = {
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530253 {{74, 18, 23}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530254 {{71, 18, 23}, {} },
255 {{71, 12, 17}, {} },
256 {{71, 6, 11}, {} },
257 },
258};
259
260static const struct cpr3_fuse_param
261sdm632_apss_target_quot_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
262 [CPR4_APSS_POWER_CLUSTER_ID] = {
263 {{75, 44, 55}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530264 {{72, 44, 55}, {} },
265 {{75, 20, 31}, {} },
266 {{75, 8, 19}, {} },
267 },
268 [CPR4_APSS_PERF_CLUSTER_ID] = {
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530269 {{75, 44, 55}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530270 {{72, 32, 43}, {} },
271 {{72, 20, 31}, {} },
272 {{72, 8, 19}, {} },
273 },
274};
275
276static const struct cpr3_fuse_param
277sdm632_apss_quot_offset_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
278 [CPR4_APSS_POWER_CLUSTER_ID] = {
279 {{} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530280 {{71, 46, 52}, {} },
281 {{74, 32, 38}, {} },
282 {{74, 24, 30}, {} },
283 },
284 [CPR4_APSS_PERF_CLUSTER_ID] = {
285 {{} },
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530286 {{74, 39, 45}, {} },
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530287 {{71, 39, 45}, {} },
288 {{71, 32, 38}, {} },
289 },
290};
291
292/*
Tirupathi Reddy53d99a02016-08-08 17:04:23 +0530293 * The maximum number of fuse combinations possible for the selected fuse
294 * parameters in fuse combo map logic.
295 * Here, possible speed-bin values = 8, fuse revision values = 8, and foundry
296 * identifier values = 8. Total number of combinations = 512 (i.e., 8 * 8 * 8)
297 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530298#define CPR4_APSS_FUSE_COMBO_MAP_MAX_COUNT 512
Tirupathi Reddy53d99a02016-08-08 17:04:23 +0530299
300
301/*
David Collins7370f1a2017-01-18 16:21:53 -0800302 * The number of possible values for misc fuse is
303 * 2^(#bits defined for misc fuse)
304 */
305#define MSM8953_MISC_FUSE_VAL_COUNT BIT(1)
306
307/*
308 * Open loop voltage fuse reference voltages in microvolts for MSM8953
309 */
310static const int msm8953_apss_fuse_ref_volt
311 [MSM8953_APSS_FUSE_CORNERS] = {
312 645000,
313 720000,
314 865000,
315 1065000,
316};
317
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530318/*
319 * Open loop voltage fuse reference voltages in microvolts for SDM632
320 */
321static const int
322sdm632_apss_fuse_ref_volt[2][SDM632_POWER_APSS_FUSE_CORNERS] = {
323 [CPR4_APSS_POWER_CLUSTER_ID] = {
324 645000,
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530325 790000,
326 865000,
327 1065000,
328 },
329 [CPR4_APSS_PERF_CLUSTER_ID] = {
Tirupathi Reddy037708a2018-04-18 14:53:24 +0530330 645000,
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530331 790000,
332 865000,
333 1065000,
334 },
335};
336
337#define CPR4_APSS_FUSE_STEP_VOLT 10000
338#define CPR4_APSS_VOLTAGE_FUSE_SIZE 6
339#define CPR4_APSS_QUOT_OFFSET_SCALE 5
David Collins7370f1a2017-01-18 16:21:53 -0800340
341#define MSM8953_APSS_CPR_SENSOR_COUNT 13
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530342#define SDM632_APSS_CPR_SENSOR_COUNT 16
343#define SDM632_APSS_THREAD0_SENSOR_MIN 0
344#define SDM632_APSS_THREAD0_SENSOR_MAX 6
345#define SDM632_APSS_THREAD1_SENSOR_MIN 7
346#define SDM632_APSS_THREAD1_SENSOR_MAX 15
David Collins7370f1a2017-01-18 16:21:53 -0800347
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530348#define CPR4_APSS_CPR_CLOCK_RATE 19200000
David Collins7370f1a2017-01-18 16:21:53 -0800349
350#define MSM8953_APSS_MAX_TEMP_POINTS 3
351#define MSM8953_APSS_TEMP_SENSOR_ID_START 4
352#define MSM8953_APSS_TEMP_SENSOR_ID_END 13
353/*
354 * Boost voltage fuse reference and ceiling voltages in microvolts for
355 * MSM8953.
356 */
357#define MSM8953_APSS_BOOST_FUSE_REF_VOLT 1140000
358#define MSM8953_APSS_BOOST_CEILING_VOLT 1140000
359#define MSM8953_APSS_BOOST_FLOOR_VOLT 900000
360#define MAX_BOOST_CONFIG_FUSE_VALUE 8
361
362#define MSM8953_APSS_CPR_SDELTA_CORE_COUNT 15
363
364/*
365 * Array of integer values mapped to each of the boost config fuse values to
366 * indicate boost enable/disable status.
367 */
368static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1};
369
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +0530370/* CPR Aging parameters for msm8953 */
371#define MSM8953_APSS_AGING_INIT_QUOT_DIFF_SCALE 1
372#define MSM8953_APSS_AGING_INIT_QUOT_DIFF_SIZE 8
373#define MSM8953_APSS_AGING_SENSOR_ID 6
374
375/* Use a very high value for max aging margin to be applied */
376#define MSM8953_APSS_AGING_MAX_AGE_MARGIN_QUOT (-1000)
377
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530378/*
379 * SOC IDs
380 */
381enum soc_id {
382 MSM8953_SOC_ID = 1,
383 SDM632_SOC_ID = 2,
384};
385
David Collins7370f1a2017-01-18 16:21:53 -0800386/**
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530387 * cpr4_msm8953_apss_read_fuse_data() - load MSM8953 APSS specific fuse
388 * parameter values
David Collins7370f1a2017-01-18 16:21:53 -0800389 * @vreg: Pointer to the CPR3 regulator
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530390 * @fuse: APSS specific fuse data
David Collins7370f1a2017-01-18 16:21:53 -0800391 *
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530392 * This function fills cpr4_apss_fuses struct with values read out of hardware
393 * fuses.
David Collins7370f1a2017-01-18 16:21:53 -0800394 *
395 * Return: 0 on success, errno on failure
396 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530397static int cpr4_msm8953_apss_read_fuse_data(struct cpr3_regulator *vreg,
398 struct cpr4_apss_fuses *fuse)
David Collins7370f1a2017-01-18 16:21:53 -0800399{
400 void __iomem *base = vreg->thread->ctrl->fuse_base;
David Collins7370f1a2017-01-18 16:21:53 -0800401 int i, rc;
402
David Collins7370f1a2017-01-18 16:21:53 -0800403 rc = cpr3_read_fuse_param(base, msm8953_misc_fuse_volt_adj_param,
404 &fuse->misc);
405 if (rc) {
406 cpr3_err(vreg, "Unable to read misc voltage adjustment fuse, rc=%d\n",
407 rc);
408 return rc;
409 }
410 cpr3_info(vreg, "CPR misc fuse value = %llu\n", fuse->misc);
411 if (fuse->misc >= MSM8953_MISC_FUSE_VAL_COUNT) {
412 cpr3_err(vreg, "CPR misc fuse value = %llu, should be < %lu\n",
413 fuse->misc, MSM8953_MISC_FUSE_VAL_COUNT);
414 return -EINVAL;
415 }
416
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +0530417 rc = cpr3_read_fuse_param(base, msm8953_apss_aging_init_quot_diff_param,
418 &fuse->aging_init_quot_diff);
419 if (rc) {
420 cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
421 rc);
422 return rc;
423 }
424
David Collins7370f1a2017-01-18 16:21:53 -0800425 for (i = 0; i < MSM8953_APSS_FUSE_CORNERS; i++) {
426 rc = cpr3_read_fuse_param(base,
427 msm8953_apss_init_voltage_param[i],
428 &fuse->init_voltage[i]);
429 if (rc) {
430 cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
431 i, rc);
432 return rc;
433 }
434
435 rc = cpr3_read_fuse_param(base,
436 msm8953_apss_target_quot_param[i],
437 &fuse->target_quot[i]);
438 if (rc) {
439 cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n",
440 i, rc);
441 return rc;
442 }
443
444 rc = cpr3_read_fuse_param(base,
445 msm8953_apss_ro_sel_param[i],
446 &fuse->ro_sel[i]);
447 if (rc) {
448 cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n",
449 i, rc);
450 return rc;
451 }
452
453 rc = cpr3_read_fuse_param(base,
454 msm8953_apss_quot_offset_param[i],
455 &fuse->quot_offset[i]);
456 if (rc) {
457 cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n",
458 i, rc);
459 return rc;
460 }
461 }
462
463 rc = cpr3_read_fuse_param(base, msm8953_cpr_boost_fuse_cfg_param,
464 &fuse->boost_cfg);
465 if (rc) {
466 cpr3_err(vreg, "Unable to read CPR boost config fuse, rc=%d\n",
467 rc);
468 return rc;
469 }
470 cpr3_info(vreg, "Voltage boost fuse config = %llu boost = %s\n",
471 fuse->boost_cfg, boost_fuse[fuse->boost_cfg]
472 ? "enable" : "disable");
473
474 rc = cpr3_read_fuse_param(base,
475 msm8953_apss_boost_fuse_volt_param,
476 &fuse->boost_voltage);
477 if (rc) {
478 cpr3_err(vreg, "failed to read boost fuse voltage, rc=%d\n",
479 rc);
480 return rc;
481 }
482
483 vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
484 if (vreg->fuse_combo >= CPR4_MSM8953_APSS_FUSE_COMBO_COUNT) {
485 cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
486 vreg->fuse_combo);
487 return -EINVAL;
488 }
489
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530490 return 0;
491}
492
493/**
494 * cpr4_sdm632_apss_read_fuse_data() - load SDM632 APSS specific fuse
495 * parameter values
496 * @vreg: Pointer to the CPR3 regulator
497 * @fuse: APSS specific fuse data
498 *
499 * This function fills cpr4_apss_fuses struct with values read out of hardware
500 * fuses.
501 *
502 * Return: 0 on success, errno on failure
503 */
504static int cpr4_sdm632_apss_read_fuse_data(struct cpr3_regulator *vreg,
505 struct cpr4_apss_fuses *fuse)
506{
507 void __iomem *base = vreg->thread->ctrl->fuse_base;
508 int i, id, rc, fuse_corners;
509
510 id = vreg->thread->thread_id;
511 if (id == CPR4_APSS_POWER_CLUSTER_ID)
512 fuse_corners = SDM632_POWER_APSS_FUSE_CORNERS;
513 else
514 fuse_corners = SDM632_PERF_APSS_FUSE_CORNERS;
515
516 for (i = 0; i < fuse_corners; i++) {
517 rc = cpr3_read_fuse_param(base,
518 sdm632_apss_init_voltage_param[id][i],
519 &fuse->init_voltage[i]);
520 if (rc) {
521 cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
522 i, rc);
523 return rc;
524 }
525
526 rc = cpr3_read_fuse_param(base,
527 sdm632_apss_target_quot_param[id][i],
528 &fuse->target_quot[i]);
529 if (rc) {
530 cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n",
531 i, rc);
532 return rc;
533 }
534
535 rc = cpr3_read_fuse_param(base,
536 sdm632_apss_ro_sel_param[id][i],
537 &fuse->ro_sel[i]);
538 if (rc) {
539 cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n",
540 i, rc);
541 return rc;
542 }
543
544 rc = cpr3_read_fuse_param(base,
545 sdm632_apss_quot_offset_param[id][i],
546 &fuse->quot_offset[i]);
547 if (rc) {
548 cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n",
549 i, rc);
550 return rc;
551 }
552 }
553
554 vreg->fuse_combo = fuse->cpr_fusing_rev + (8 * fuse->speed_bin);
555 if (vreg->fuse_combo >= CPR4_SDM632_APSS_FUSE_COMBO_COUNT) {
556 cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
557 vreg->fuse_combo);
558 return -EINVAL;
559 }
560
561 return 0;
562}
563
564/**
565 * cpr4_apss_read_fuse_data() - load APSS specific fuse parameter values
566 * @vreg: Pointer to the CPR3 regulator
567 *
568 * This function allocates a cpr4_apss_fuses struct, fills it with
569 * values read out of hardware fuses, and finally copies common fuse values
570 * into the CPR3 regulator struct.
571 *
572 * Return: 0 on success, errno on failure
573 */
574static int cpr4_apss_read_fuse_data(struct cpr3_regulator *vreg)
575{
576 void __iomem *base = vreg->thread->ctrl->fuse_base;
577 struct cpr4_apss_fuses *fuse;
578 int rc, fuse_corners;
579 enum soc_id soc_revision;
580
581 fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL);
582 if (!fuse)
583 return -ENOMEM;
584
585 soc_revision = vreg->thread->ctrl->soc_revision;
586 switch (soc_revision) {
587 case MSM8953_SOC_ID:
588 fuse_corners = MSM8953_APSS_FUSE_CORNERS;
589 break;
590 case SDM632_SOC_ID:
591 if (vreg->thread->thread_id == CPR4_APSS_POWER_CLUSTER_ID)
592 fuse_corners = SDM632_POWER_APSS_FUSE_CORNERS;
593 else
594 fuse_corners = SDM632_PERF_APSS_FUSE_CORNERS;
595 break;
596 default:
597 cpr3_err(vreg, "unsupported soc id = %d\n", soc_revision);
598 return -EINVAL;
599 }
600
601 fuse->ro_sel = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
602 sizeof(*fuse->ro_sel), GFP_KERNEL);
603 fuse->init_voltage = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
604 sizeof(*fuse->init_voltage), GFP_KERNEL);
605 fuse->target_quot = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
606 sizeof(*fuse->target_quot), GFP_KERNEL);
607 fuse->quot_offset = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
608 sizeof(*fuse->quot_offset), GFP_KERNEL);
609
610 if (!fuse->ro_sel || !fuse->init_voltage || !fuse->target_quot
611 || !fuse->quot_offset)
612 return -ENOMEM;
613
614 rc = cpr3_read_fuse_param(base, msm8953_apss_speed_bin_param,
615 &fuse->speed_bin);
616 if (rc) {
617 cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc);
618 return rc;
619 }
620
621 rc = cpr3_read_fuse_param(base, msm8953_cpr_fusing_rev_param,
622 &fuse->cpr_fusing_rev);
623 if (rc) {
624 cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
625 rc);
626 return rc;
627 }
628
629 rc = cpr3_read_fuse_param(base, msm8953_apss_foundry_id_param,
630 &fuse->foundry_id);
631 if (rc) {
632 cpr3_err(vreg, "Unable to read foundry id fuse, rc=%d\n", rc);
633 return rc;
634 }
635 cpr3_info(vreg, "speed bin = %llu, CPR fusing revision = %llu, foundry id = %llu\n",
636 fuse->speed_bin, fuse->cpr_fusing_rev,
637 fuse->foundry_id);
638
639 switch (soc_revision) {
640 case MSM8953_SOC_ID:
641 rc = cpr4_msm8953_apss_read_fuse_data(vreg, fuse);
642 if (rc) {
643 cpr3_err(vreg, "msm8953 apss fuse data read failed, rc=%d\n",
644 rc);
645 return rc;
646 }
647 break;
648 case SDM632_SOC_ID:
649 rc = cpr4_sdm632_apss_read_fuse_data(vreg, fuse);
650 if (rc) {
651 cpr3_err(vreg, "sdm632 apss fuse data read failed, rc=%d\n",
652 rc);
653 return rc;
654 }
655 break;
656 default:
657 cpr3_err(vreg, "unsupported soc id = %d\n", soc_revision);
658 return -EINVAL;
659 }
660
David Collins7370f1a2017-01-18 16:21:53 -0800661 vreg->speed_bin_fuse = fuse->speed_bin;
662 vreg->cpr_rev_fuse = fuse->cpr_fusing_rev;
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530663 vreg->fuse_corner_count = fuse_corners;
David Collins7370f1a2017-01-18 16:21:53 -0800664 vreg->platform_fuses = fuse;
665
666 return 0;
667}
668
669/**
670 * cpr4_apss_parse_corner_data() - parse APSS corner data from device tree
671 * properties of the CPR3 regulator's device node
672 * @vreg: Pointer to the CPR3 regulator
673 *
674 * Return: 0 on success, errno on failure
675 */
676static int cpr4_apss_parse_corner_data(struct cpr3_regulator *vreg)
677{
678 int rc;
679
680 rc = cpr3_parse_common_corner_data(vreg);
681 if (rc) {
682 cpr3_err(vreg, "error reading corner data, rc=%d\n", rc);
683 return rc;
684 }
685
686 return rc;
687}
688
689/**
690 * cpr4_apss_parse_misc_fuse_voltage_adjustments() - fill an array from a
691 * portion of the voltage adjustments specified based on
692 * miscellaneous fuse bits.
693 * @vreg: Pointer to the CPR3 regulator
694 * @volt_adjust: Voltage adjustment output data array which must be
695 * of size vreg->corner_count
696 *
697 * cpr3_parse_common_corner_data() must be called for vreg before this function
698 * is called so that speed bin size elements are initialized.
699 *
700 * Two formats are supported for the device tree property:
701 * 1. Length == tuple_list_size * vreg->corner_count
702 * (reading begins at index 0)
703 * 2. Length == tuple_list_size * vreg->speed_bin_corner_sum
704 * (reading begins at index tuple_list_size * vreg->speed_bin_offset)
705 *
706 * Here, tuple_list_size is the number of possible values for misc fuse.
707 * All other property lengths are treated as errors.
708 *
709 * Return: 0 on success, errno on failure
710 */
711static int cpr4_apss_parse_misc_fuse_voltage_adjustments(
712 struct cpr3_regulator *vreg, u32 *volt_adjust)
713{
714 struct device_node *node = vreg->of_node;
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530715 struct cpr4_apss_fuses *fuse = vreg->platform_fuses;
David Collins7370f1a2017-01-18 16:21:53 -0800716 int tuple_list_size = MSM8953_MISC_FUSE_VAL_COUNT;
717 int i, offset, rc, len = 0;
718 const char *prop_name = "qcom,cpr-misc-fuse-voltage-adjustment";
719
720 if (!of_find_property(node, prop_name, &len)) {
721 cpr3_err(vreg, "property %s is missing\n", prop_name);
722 return -EINVAL;
723 }
724
725 if (len == tuple_list_size * vreg->corner_count * sizeof(u32)) {
726 offset = 0;
727 } else if (vreg->speed_bin_corner_sum > 0 &&
728 len == tuple_list_size * vreg->speed_bin_corner_sum
729 * sizeof(u32)) {
730 offset = tuple_list_size * vreg->speed_bin_offset
731 + fuse->misc * vreg->corner_count;
732 } else {
733 if (vreg->speed_bin_corner_sum > 0)
734 cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n",
735 prop_name, len,
736 tuple_list_size * vreg->corner_count
737 * sizeof(u32),
738 tuple_list_size * vreg->speed_bin_corner_sum
739 * sizeof(u32));
740 else
741 cpr3_err(vreg, "property %s has invalid length=%d, should be %zu\n",
742 prop_name, len,
743 tuple_list_size * vreg->corner_count
744 * sizeof(u32));
745 return -EINVAL;
746 }
747
748 for (i = 0; i < vreg->corner_count; i++) {
749 rc = of_property_read_u32_index(node, prop_name, offset + i,
750 &volt_adjust[i]);
751 if (rc) {
752 cpr3_err(vreg, "error reading property %s, rc=%d\n",
753 prop_name, rc);
754 return rc;
755 }
756 }
757
758 return 0;
759}
760
761/**
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530762 * cpr4_apss_calculate_open_loop_voltages() - calculate the open-loop
David Collins7370f1a2017-01-18 16:21:53 -0800763 * voltage for each corner of a CPR3 regulator
764 * @vreg: Pointer to the CPR3 regulator
765 *
766 * If open-loop voltage interpolation is allowed in device tree, then
767 * this function calculates the open-loop voltage for a given corner using
768 * linear interpolation. This interpolation is performed using the processor
769 * frequencies of the lower and higher Fmax corners along with their fused
770 * open-loop voltages.
771 *
772 * If open-loop voltage interpolation is not allowed, then this function uses
773 * the Fmax fused open-loop voltage for all of the corners associated with a
774 * given fuse corner.
775 *
776 * Return: 0 on success, errno on failure
777 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530778static int cpr4_apss_calculate_open_loop_voltages(struct cpr3_regulator *vreg)
David Collins7370f1a2017-01-18 16:21:53 -0800779{
780 struct device_node *node = vreg->of_node;
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530781 struct cpr4_apss_fuses *fuse = vreg->platform_fuses;
782 int i, j, id, rc = 0;
David Collins7370f1a2017-01-18 16:21:53 -0800783 bool allow_interpolation;
784 u64 freq_low, volt_low, freq_high, volt_high;
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530785 const int *ref_volt;
David Collins7370f1a2017-01-18 16:21:53 -0800786 int *fuse_volt, *misc_adj_volt;
787 int *fmax_corner;
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530788 const char * const *corner_name;
789 enum soc_id soc_revision;
David Collins7370f1a2017-01-18 16:21:53 -0800790
791 fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
792 GFP_KERNEL);
793 fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
794 GFP_KERNEL);
795 if (!fuse_volt || !fmax_corner) {
796 rc = -ENOMEM;
797 goto done;
798 }
799
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530800 id = vreg->thread->thread_id;
801 soc_revision = vreg->thread->ctrl->soc_revision;
802
803 switch (soc_revision) {
804 case MSM8953_SOC_ID:
805 ref_volt = msm8953_apss_fuse_ref_volt;
806 corner_name = cpr4_msm8953_apss_fuse_corner_name;
807 break;
808 case SDM632_SOC_ID:
809 ref_volt = sdm632_apss_fuse_ref_volt[id];
810 if (id == CPR4_APSS_POWER_CLUSTER_ID)
811 corner_name = cpr4_sdm632_power_apss_fuse_corner_name;
812 else
813 corner_name = cpr4_sdm632_perf_apss_fuse_corner_name;
814 break;
815 default:
816 cpr3_err(vreg, "unsupported soc id = %d\n", soc_revision);
817 rc = -EINVAL;
818 goto done;
819 }
820
David Collins7370f1a2017-01-18 16:21:53 -0800821 for (i = 0; i < vreg->fuse_corner_count; i++) {
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530822 fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(ref_volt[i],
823 CPR4_APSS_FUSE_STEP_VOLT, fuse->init_voltage[i],
824 CPR4_APSS_VOLTAGE_FUSE_SIZE);
David Collins7370f1a2017-01-18 16:21:53 -0800825
826 /* Log fused open-loop voltage values for debugging purposes. */
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530827 cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", corner_name[i],
David Collins7370f1a2017-01-18 16:21:53 -0800828 fuse_volt[i]);
829 }
830
831 rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt);
832 if (rc) {
833 cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n",
834 rc);
835 goto done;
836 }
837
838 allow_interpolation = of_property_read_bool(node,
839 "qcom,allow-voltage-interpolation");
840
841 for (i = 1; i < vreg->fuse_corner_count; i++) {
842 if (fuse_volt[i] < fuse_volt[i - 1]) {
843 cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n",
844 i, fuse_volt[i], i - 1, fuse_volt[i - 1],
845 i, fuse_volt[i - 1]);
846 fuse_volt[i] = fuse_volt[i - 1];
847 }
848 }
849
850 if (!allow_interpolation) {
851 /* Use fused open-loop voltage for lower frequencies. */
852 for (i = 0; i < vreg->corner_count; i++)
853 vreg->corner[i].open_loop_volt
854 = fuse_volt[vreg->corner[i].cpr_fuse_corner];
855 goto done;
856 }
857
858 /* Determine highest corner mapped to each fuse corner */
859 j = vreg->fuse_corner_count - 1;
860 for (i = vreg->corner_count - 1; i >= 0; i--) {
861 if (vreg->corner[i].cpr_fuse_corner == j) {
862 fmax_corner[j] = i;
863 j--;
864 }
865 }
866 if (j >= 0) {
867 cpr3_err(vreg, "invalid fuse corner mapping\n");
868 rc = -EINVAL;
869 goto done;
870 }
871
872 /*
873 * Interpolation is not possible for corners mapped to the lowest fuse
874 * corner so use the fuse corner value directly.
875 */
876 for (i = 0; i <= fmax_corner[0]; i++)
877 vreg->corner[i].open_loop_volt = fuse_volt[0];
878
879 /* Interpolate voltages for the higher fuse corners. */
880 for (i = 1; i < vreg->fuse_corner_count; i++) {
881 freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
882 volt_low = fuse_volt[i - 1];
883 freq_high = vreg->corner[fmax_corner[i]].proc_freq;
884 volt_high = fuse_volt[i];
885
886 for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
887 vreg->corner[j].open_loop_volt = cpr3_interpolate(
888 freq_low, volt_low, freq_high, volt_high,
889 vreg->corner[j].proc_freq);
890 }
891
892done:
893 if (rc == 0) {
894 cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n");
895 for (i = 0; i < vreg->corner_count; i++)
896 cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i,
897 vreg->corner[i].open_loop_volt);
898
899 rc = cpr3_adjust_open_loop_voltages(vreg);
900 if (rc)
901 cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
902 rc);
903
904 if (of_find_property(node,
905 "qcom,cpr-misc-fuse-voltage-adjustment",
906 NULL)) {
907 misc_adj_volt = kcalloc(vreg->corner_count,
908 sizeof(*misc_adj_volt), GFP_KERNEL);
909 if (!misc_adj_volt) {
910 rc = -ENOMEM;
911 goto _exit;
912 }
913
914 rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg,
915 misc_adj_volt);
916 if (rc) {
917 cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n",
918 rc);
919 kfree(misc_adj_volt);
920 goto _exit;
921 }
922
923 for (i = 0; i < vreg->corner_count; i++)
924 vreg->corner[i].open_loop_volt
925 += misc_adj_volt[i];
926 kfree(misc_adj_volt);
927 }
928 }
929
930_exit:
931 kfree(fuse_volt);
932 kfree(fmax_corner);
933 return rc;
934}
935
936/**
937 * cpr4_msm8953_apss_set_no_interpolation_quotients() - use the fused target
938 * quotient values for lower frequencies.
939 * @vreg: Pointer to the CPR3 regulator
940 * @volt_adjust: Pointer to array of per-corner closed-loop adjustment
941 * voltages
942 * @volt_adjust_fuse: Pointer to array of per-fuse-corner closed-loop
943 * adjustment voltages
944 * @ro_scale: Pointer to array of per-fuse-corner RO scaling factor
945 * values with units of QUOT/V
946 *
947 * Return: 0 on success, errno on failure
948 */
949static int cpr4_msm8953_apss_set_no_interpolation_quotients(
950 struct cpr3_regulator *vreg, int *volt_adjust,
951 int *volt_adjust_fuse, int *ro_scale)
952{
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530953 struct cpr4_apss_fuses *fuse = vreg->platform_fuses;
David Collins7370f1a2017-01-18 16:21:53 -0800954 u32 quot, ro;
955 int quot_adjust;
956 int i, fuse_corner;
957
958 for (i = 0; i < vreg->corner_count; i++) {
959 fuse_corner = vreg->corner[i].cpr_fuse_corner;
960 quot = fuse->target_quot[fuse_corner];
961 quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
962 volt_adjust_fuse[fuse_corner] +
963 volt_adjust[i]);
964 ro = fuse->ro_sel[fuse_corner];
965 vreg->corner[i].target_quot[ro] = quot + quot_adjust;
966 cpr3_debug(vreg, "corner=%d RO=%u target quot=%u\n",
967 i, ro, quot);
968
969 if (quot_adjust)
970 cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n",
971 i, ro, quot, vreg->corner[i].target_quot[ro],
972 volt_adjust_fuse[fuse_corner] +
973 volt_adjust[i]);
974 }
975
976 return 0;
977}
978
979/**
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530980 * cpr4_apss_calculate_target_quotients() - calculate the CPR target
David Collins7370f1a2017-01-18 16:21:53 -0800981 * quotient for each corner of a CPR3 regulator
982 * @vreg: Pointer to the CPR3 regulator
983 *
984 * If target quotient interpolation is allowed in device tree, then this
985 * function calculates the target quotient for a given corner using linear
986 * interpolation. This interpolation is performed using the processor
987 * frequencies of the lower and higher Fmax corners along with the fused
988 * target quotient and quotient offset of the higher Fmax corner.
989 *
990 * If target quotient interpolation is not allowed, then this function uses
991 * the Fmax fused target quotient for all of the corners associated with a
992 * given fuse corner.
993 *
994 * Return: 0 on success, errno on failure
995 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530996static int cpr4_apss_calculate_target_quotients(struct cpr3_regulator *vreg)
David Collins7370f1a2017-01-18 16:21:53 -0800997{
Tirupathi Reddy526719c2018-03-08 13:39:16 +0530998 struct cpr4_apss_fuses *fuse = vreg->platform_fuses;
David Collins7370f1a2017-01-18 16:21:53 -0800999 int rc;
1000 bool allow_interpolation;
1001 u64 freq_low, freq_high, prev_quot;
1002 u64 *quot_low;
1003 u64 *quot_high;
1004 u32 quot, ro;
1005 int i, j, fuse_corner, quot_adjust;
1006 int *fmax_corner;
1007 int *volt_adjust, *volt_adjust_fuse, *ro_scale;
1008 int *voltage_adj_misc;
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301009 int lowest_fuse_corner, highest_fuse_corner;
1010 const char * const *corner_name;
David Collins7370f1a2017-01-18 16:21:53 -08001011
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301012 switch (vreg->thread->ctrl->soc_revision) {
1013 case MSM8953_SOC_ID:
1014 corner_name = cpr4_msm8953_apss_fuse_corner_name;
1015 lowest_fuse_corner = CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS;
1016 highest_fuse_corner = CPR4_MSM8953_APSS_FUSE_CORNER_TURBO_L1;
1017 break;
1018 case SDM632_SOC_ID:
1019 if (vreg->thread->thread_id == CPR4_APSS_POWER_CLUSTER_ID) {
1020 corner_name = cpr4_sdm632_power_apss_fuse_corner_name;
1021 lowest_fuse_corner =
1022 CPR4_SDM632_POWER_APSS_FUSE_CORNER_LOWSVS;
1023 highest_fuse_corner =
1024 CPR4_SDM632_POWER_APSS_FUSE_CORNER_TURBO_L1;
1025 } else {
1026 corner_name = cpr4_sdm632_perf_apss_fuse_corner_name;
1027 lowest_fuse_corner =
Tirupathi Reddy037708a2018-04-18 14:53:24 +05301028 CPR4_SDM632_PERF_APSS_FUSE_CORNER_LOWSVS;
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301029 highest_fuse_corner =
1030 CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1;
1031 }
1032 break;
1033 default:
1034 cpr3_err(vreg, "unsupported soc id = %d\n",
1035 vreg->thread->ctrl->soc_revision);
1036 return -EINVAL;
1037 }
David Collins7370f1a2017-01-18 16:21:53 -08001038 /* Log fused quotient values for debugging purposes. */
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301039 cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu\n",
1040 corner_name[lowest_fuse_corner],
1041 fuse->ro_sel[lowest_fuse_corner],
1042 fuse->target_quot[lowest_fuse_corner]);
1043 for (i = lowest_fuse_corner + 1; i <= highest_fuse_corner; i++)
David Collins7370f1a2017-01-18 16:21:53 -08001044 cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n",
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301045 corner_name[i], fuse->ro_sel[i], fuse->target_quot[i],
David Collins7370f1a2017-01-18 16:21:53 -08001046 fuse->ro_sel[i], fuse->quot_offset[i] *
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301047 CPR4_APSS_QUOT_OFFSET_SCALE);
David Collins7370f1a2017-01-18 16:21:53 -08001048
1049 allow_interpolation = of_property_read_bool(vreg->of_node,
1050 "qcom,allow-quotient-interpolation");
1051
1052 volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust),
1053 GFP_KERNEL);
1054 volt_adjust_fuse = kcalloc(vreg->fuse_corner_count,
1055 sizeof(*volt_adjust_fuse), GFP_KERNEL);
1056 ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale),
1057 GFP_KERNEL);
1058 fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
1059 GFP_KERNEL);
1060 quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low),
1061 GFP_KERNEL);
1062 quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high),
1063 GFP_KERNEL);
1064 if (!volt_adjust || !volt_adjust_fuse || !ro_scale ||
1065 !fmax_corner || !quot_low || !quot_high) {
1066 rc = -ENOMEM;
1067 goto done;
1068 }
1069
1070 rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0],
1071 volt_adjust, volt_adjust_fuse, ro_scale);
1072 if (rc) {
1073 cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n",
1074 rc);
1075 goto done;
1076 }
1077
1078 if (of_find_property(vreg->of_node,
1079 "qcom,cpr-misc-fuse-voltage-adjustment", NULL)) {
1080 voltage_adj_misc = kcalloc(vreg->corner_count,
1081 sizeof(*voltage_adj_misc), GFP_KERNEL);
1082 if (!voltage_adj_misc) {
1083 rc = -ENOMEM;
1084 goto done;
1085 }
1086
1087 rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg,
1088 voltage_adj_misc);
1089 if (rc) {
1090 cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n",
1091 rc);
1092 kfree(voltage_adj_misc);
1093 goto done;
1094 }
1095
1096 for (i = 0; i < vreg->corner_count; i++)
1097 volt_adjust[i] += voltage_adj_misc[i];
1098
1099 kfree(voltage_adj_misc);
1100 }
1101
1102 if (!allow_interpolation) {
1103 /* Use fused target quotients for lower frequencies. */
1104 return cpr4_msm8953_apss_set_no_interpolation_quotients(
1105 vreg, volt_adjust, volt_adjust_fuse, ro_scale);
1106 }
1107
1108 /* Determine highest corner mapped to each fuse corner */
1109 j = vreg->fuse_corner_count - 1;
1110 for (i = vreg->corner_count - 1; i >= 0; i--) {
1111 if (vreg->corner[i].cpr_fuse_corner == j) {
1112 fmax_corner[j] = i;
1113 j--;
1114 }
1115 }
1116 if (j >= 0) {
1117 cpr3_err(vreg, "invalid fuse corner mapping\n");
1118 rc = -EINVAL;
1119 goto done;
1120 }
1121
1122 /*
1123 * Interpolation is not possible for corners mapped to the lowest fuse
1124 * corner so use the fuse corner value directly.
1125 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301126 i = lowest_fuse_corner;
David Collins7370f1a2017-01-18 16:21:53 -08001127 quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]);
1128 quot = fuse->target_quot[i] + quot_adjust;
1129 quot_high[i] = quot_low[i] = quot;
1130 ro = fuse->ro_sel[i];
1131 if (quot_adjust)
1132 cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n",
1133 i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]);
1134
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301135 for (i = 0; i <= fmax_corner[lowest_fuse_corner]; i++)
David Collins7370f1a2017-01-18 16:21:53 -08001136 vreg->corner[i].target_quot[ro] = quot;
1137
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301138 for (i = lowest_fuse_corner + 1; i < vreg->fuse_corner_count; i++) {
David Collins7370f1a2017-01-18 16:21:53 -08001139 quot_high[i] = fuse->target_quot[i];
1140 if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
1141 quot_low[i] = quot_high[i - 1];
1142 else
1143 quot_low[i] = quot_high[i]
1144 - fuse->quot_offset[i]
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301145 * CPR4_APSS_QUOT_OFFSET_SCALE;
David Collins7370f1a2017-01-18 16:21:53 -08001146 if (quot_high[i] < quot_low[i]) {
1147 cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n",
1148 i, quot_high[i], i, quot_low[i],
1149 i, quot_low[i]);
1150 quot_high[i] = quot_low[i];
1151 }
1152 }
1153
1154 /* Perform per-fuse-corner target quotient adjustment */
1155 for (i = 1; i < vreg->fuse_corner_count; i++) {
1156 quot_adjust = cpr3_quot_adjustment(ro_scale[i],
1157 volt_adjust_fuse[i]);
1158 if (quot_adjust) {
1159 prev_quot = quot_high[i];
1160 quot_high[i] += quot_adjust;
1161 cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n",
1162 i, fuse->ro_sel[i], prev_quot, quot_high[i],
1163 volt_adjust_fuse[i]);
1164 }
1165
1166 if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
1167 quot_low[i] = quot_high[i - 1];
1168 else
1169 quot_low[i] += cpr3_quot_adjustment(ro_scale[i],
1170 volt_adjust_fuse[i - 1]);
1171
1172 if (quot_high[i] < quot_low[i]) {
1173 cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n",
1174 i, quot_high[i], i, quot_low[i],
1175 i, quot_low[i]);
1176 quot_high[i] = quot_low[i];
1177 }
1178 }
1179
1180 /* Interpolate voltages for the higher fuse corners. */
1181 for (i = 1; i < vreg->fuse_corner_count; i++) {
1182 freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
1183 freq_high = vreg->corner[fmax_corner[i]].proc_freq;
1184
1185 ro = fuse->ro_sel[i];
1186 for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
1187 vreg->corner[j].target_quot[ro] = cpr3_interpolate(
1188 freq_low, quot_low[i], freq_high, quot_high[i],
1189 vreg->corner[j].proc_freq);
1190 }
1191
1192 /* Perform per-corner target quotient adjustment */
1193 for (i = 0; i < vreg->corner_count; i++) {
1194 fuse_corner = vreg->corner[i].cpr_fuse_corner;
1195 ro = fuse->ro_sel[fuse_corner];
1196 quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
1197 volt_adjust[i]);
1198 if (quot_adjust) {
1199 prev_quot = vreg->corner[i].target_quot[ro];
1200 vreg->corner[i].target_quot[ro] += quot_adjust;
1201 cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n",
1202 i, ro, prev_quot,
1203 vreg->corner[i].target_quot[ro],
1204 volt_adjust[i]);
1205 }
1206 }
1207
1208 /* Ensure that target quotients increase monotonically */
1209 for (i = 1; i < vreg->corner_count; i++) {
1210 ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner];
1211 if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro
1212 && vreg->corner[i].target_quot[ro]
1213 < vreg->corner[i - 1].target_quot[ro]) {
1214 cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n",
1215 i, ro, vreg->corner[i].target_quot[ro],
1216 i - 1, ro, vreg->corner[i - 1].target_quot[ro],
1217 i, ro, vreg->corner[i - 1].target_quot[ro]);
1218 vreg->corner[i].target_quot[ro]
1219 = vreg->corner[i - 1].target_quot[ro];
1220 }
1221 }
1222
1223done:
1224 kfree(volt_adjust);
1225 kfree(volt_adjust_fuse);
1226 kfree(ro_scale);
1227 kfree(fmax_corner);
1228 kfree(quot_low);
1229 kfree(quot_high);
1230 return rc;
1231}
1232
1233/**
1234 * cpr4_apss_print_settings() - print out APSS CPR configuration settings into
1235 * the kernel log for debugging purposes
1236 * @vreg: Pointer to the CPR3 regulator
1237 */
1238static void cpr4_apss_print_settings(struct cpr3_regulator *vreg)
1239{
1240 struct cpr3_corner *corner;
1241 int i;
1242
1243 cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n");
1244 for (i = 0; i < vreg->corner_count; i++) {
1245 corner = &vreg->corner[i];
1246 cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n",
1247 i, corner->proc_freq, corner->cpr_fuse_corner,
1248 corner->floor_volt, corner->open_loop_volt,
1249 corner->ceiling_volt);
1250 }
1251
1252 if (vreg->thread->ctrl->apm)
1253 cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n",
1254 vreg->thread->ctrl->apm_threshold_volt,
1255 vreg->thread->ctrl->apm_adj_volt);
1256}
1257
1258/**
1259 * cpr4_apss_init_thread() - perform steps necessary to initialize the
1260 * configuration data for a CPR3 thread
1261 * @thread: Pointer to the CPR3 thread
1262 *
1263 * Return: 0 on success, errno on failure
1264 */
1265static int cpr4_apss_init_thread(struct cpr3_thread *thread)
1266{
1267 int rc;
1268
1269 rc = cpr3_parse_common_thread_data(thread);
1270 if (rc) {
1271 cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n",
1272 thread->thread_id, rc);
1273 return rc;
1274 }
1275
1276 return 0;
1277}
1278
1279/**
1280 * cpr4_apss_parse_temp_adj_properties() - parse temperature based
1281 * adjustment properties from device tree.
1282 * @ctrl: Pointer to the CPR3 controller
1283 *
1284 * Return: 0 on success, errno on failure
1285 */
1286static int cpr4_apss_parse_temp_adj_properties(struct cpr3_controller *ctrl)
1287{
1288 struct device_node *of_node = ctrl->dev->of_node;
1289 int rc, i, len, temp_point_count;
1290
1291 if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) {
1292 /*
1293 * Temperature based adjustments are not defined. Single
1294 * temperature band is still valid for per-online-core
1295 * adjustments.
1296 */
1297 ctrl->temp_band_count = 1;
1298 return 0;
1299 }
1300
1301 temp_point_count = len / sizeof(u32);
1302 if (temp_point_count <= 0
1303 || temp_point_count > MSM8953_APSS_MAX_TEMP_POINTS) {
1304 cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n",
1305 temp_point_count, MSM8953_APSS_MAX_TEMP_POINTS);
1306 return -EINVAL;
1307 }
1308
1309 ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count,
1310 sizeof(*ctrl->temp_points), GFP_KERNEL);
1311 if (!ctrl->temp_points)
1312 return -ENOMEM;
1313
1314 rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map",
1315 ctrl->temp_points, temp_point_count);
1316 if (rc) {
1317 cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n",
1318 rc);
1319 return rc;
1320 }
1321
1322 for (i = 0; i < temp_point_count; i++)
1323 cpr3_debug(ctrl, "Temperature Point %d=%d\n", i,
1324 ctrl->temp_points[i]);
1325
1326 /*
1327 * If t1, t2, and t3 are the temperature points, then the temperature
1328 * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).
1329 */
1330 ctrl->temp_band_count = temp_point_count + 1;
1331 cpr3_debug(ctrl, "Number of temp bands =%d\n", ctrl->temp_band_count);
1332
1333 rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band",
1334 &ctrl->initial_temp_band);
1335 if (rc) {
1336 cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n",
1337 rc);
1338 return rc;
1339 }
1340
1341 if (ctrl->initial_temp_band >= ctrl->temp_band_count) {
1342 cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n",
1343 ctrl->initial_temp_band, ctrl->temp_band_count - 1);
1344 return -EINVAL;
1345 }
1346
1347 ctrl->temp_sensor_id_start = MSM8953_APSS_TEMP_SENSOR_ID_START;
1348 ctrl->temp_sensor_id_end = MSM8953_APSS_TEMP_SENSOR_ID_END;
1349 ctrl->allow_temp_adj = true;
1350 return rc;
1351}
1352
1353/**
1354 * cpr4_apss_parse_boost_properties() - parse configuration data for boost
1355 * voltage adjustment for CPR3 regulator from device tree.
1356 * @vreg: Pointer to the CPR3 regulator
1357 *
1358 * Return: 0 on success, errno on failure
1359 */
1360static int cpr4_apss_parse_boost_properties(struct cpr3_regulator *vreg)
1361{
1362 struct cpr3_controller *ctrl = vreg->thread->ctrl;
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301363 struct cpr4_apss_fuses *fuse = vreg->platform_fuses;
David Collins7370f1a2017-01-18 16:21:53 -08001364 struct cpr3_corner *corner;
1365 int i, boost_voltage, final_boost_volt, rc = 0;
1366 int *boost_table = NULL, *boost_temp_adj = NULL;
1367 int boost_voltage_adjust = 0, boost_num_cores = 0;
1368 u32 boost_allowed = 0;
1369
1370 if (!boost_fuse[fuse->boost_cfg])
1371 /* Voltage boost is disabled in fuse */
1372 return 0;
1373
1374 if (of_find_property(vreg->of_node, "qcom,allow-boost", NULL)) {
1375 rc = cpr3_parse_array_property(vreg, "qcom,allow-boost", 1,
1376 &boost_allowed);
1377 if (rc)
1378 return rc;
1379 }
1380
1381 if (!boost_allowed) {
1382 /* Voltage boost is not enabled for this regulator */
1383 return 0;
1384 }
1385
1386 boost_voltage = cpr3_convert_open_loop_voltage_fuse(
1387 MSM8953_APSS_BOOST_FUSE_REF_VOLT,
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301388 CPR4_APSS_FUSE_STEP_VOLT,
David Collins7370f1a2017-01-18 16:21:53 -08001389 fuse->boost_voltage,
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301390 CPR4_APSS_VOLTAGE_FUSE_SIZE);
David Collins7370f1a2017-01-18 16:21:53 -08001391
1392 /* Log boost voltage value for debugging purposes. */
1393 cpr3_info(vreg, "Boost open-loop=%7d uV\n", boost_voltage);
1394
1395 if (of_find_property(vreg->of_node,
1396 "qcom,cpr-boost-voltage-fuse-adjustment", NULL)) {
1397 rc = cpr3_parse_array_property(vreg,
1398 "qcom,cpr-boost-voltage-fuse-adjustment",
1399 1, &boost_voltage_adjust);
1400 if (rc) {
1401 cpr3_err(vreg, "qcom,cpr-boost-voltage-fuse-adjustment reading failed, rc=%d\n",
1402 rc);
1403 return rc;
1404 }
1405
1406 boost_voltage += boost_voltage_adjust;
1407 /* Log boost voltage value for debugging purposes. */
1408 cpr3_info(vreg, "Adjusted boost open-loop=%7d uV\n",
1409 boost_voltage);
1410 }
1411
1412 /* Limit boost voltage value between ceiling and floor voltage limits */
1413 boost_voltage = min(boost_voltage, MSM8953_APSS_BOOST_CEILING_VOLT);
1414 boost_voltage = max(boost_voltage, MSM8953_APSS_BOOST_FLOOR_VOLT);
1415
1416 /*
1417 * The boost feature can only be used for the highest voltage corner.
1418 * Also, keep core-count adjustments disabled when the boost feature
1419 * is enabled.
1420 */
1421 corner = &vreg->corner[vreg->corner_count - 1];
1422 if (!corner->sdelta) {
1423 /*
1424 * If core-count/temp adjustments are not defined, the cpr4
1425 * sdelta for this corner will not be allocated. Allocate it
1426 * here for boost configuration.
1427 */
1428 corner->sdelta = devm_kzalloc(ctrl->dev,
1429 sizeof(*corner->sdelta), GFP_KERNEL);
1430 if (!corner->sdelta)
1431 return -ENOMEM;
1432 }
1433 corner->sdelta->temp_band_count = ctrl->temp_band_count;
1434
1435 rc = of_property_read_u32(vreg->of_node, "qcom,cpr-num-boost-cores",
1436 &boost_num_cores);
1437 if (rc) {
1438 cpr3_err(vreg, "qcom,cpr-num-boost-cores reading failed, rc=%d\n",
1439 rc);
1440 return rc;
1441 }
1442
1443 if (boost_num_cores <= 0
1444 || boost_num_cores > MSM8953_APSS_CPR_SDELTA_CORE_COUNT) {
1445 cpr3_err(vreg, "Invalid boost number of cores = %d\n",
1446 boost_num_cores);
1447 return -EINVAL;
1448 }
1449 corner->sdelta->boost_num_cores = boost_num_cores;
1450
1451 boost_table = devm_kcalloc(ctrl->dev, corner->sdelta->temp_band_count,
1452 sizeof(*boost_table), GFP_KERNEL);
1453 if (!boost_table)
1454 return -ENOMEM;
1455
1456 if (of_find_property(vreg->of_node,
1457 "qcom,cpr-boost-temp-adjustment", NULL)) {
1458 boost_temp_adj = kcalloc(corner->sdelta->temp_band_count,
1459 sizeof(*boost_temp_adj), GFP_KERNEL);
1460 if (!boost_temp_adj)
1461 return -ENOMEM;
1462
1463 rc = cpr3_parse_array_property(vreg,
1464 "qcom,cpr-boost-temp-adjustment",
1465 corner->sdelta->temp_band_count,
1466 boost_temp_adj);
1467 if (rc) {
1468 cpr3_err(vreg, "qcom,cpr-boost-temp-adjustment reading failed, rc=%d\n",
1469 rc);
1470 goto done;
1471 }
1472 }
1473
1474 for (i = 0; i < corner->sdelta->temp_band_count; i++) {
1475 /* Apply static adjustments to boost voltage */
1476 final_boost_volt = boost_voltage + (boost_temp_adj == NULL
1477 ? 0 : boost_temp_adj[i]);
1478 /*
1479 * Limit final adjusted boost voltage value between ceiling
1480 * and floor voltage limits
1481 */
1482 final_boost_volt = min(final_boost_volt,
1483 MSM8953_APSS_BOOST_CEILING_VOLT);
1484 final_boost_volt = max(final_boost_volt,
1485 MSM8953_APSS_BOOST_FLOOR_VOLT);
1486
1487 boost_table[i] = (corner->open_loop_volt - final_boost_volt)
1488 / ctrl->step_volt;
1489 cpr3_debug(vreg, "Adjusted boost voltage margin for temp band %d = %d steps\n",
1490 i, boost_table[i]);
1491 }
1492
1493 corner->ceiling_volt = MSM8953_APSS_BOOST_CEILING_VOLT;
1494 corner->sdelta->boost_table = boost_table;
1495 corner->sdelta->allow_boost = true;
1496 corner->sdelta->allow_core_count_adj = false;
1497 vreg->allow_boost = true;
1498 ctrl->allow_boost = true;
1499done:
1500 kfree(boost_temp_adj);
1501 return rc;
1502}
1503
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301504/*
1505 * Constants which define the selection fuse parameters used in fuse combo map
1506 * logic.
1507 */
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301508enum cpr4_apss_fuse_combo_parameters {
1509 CPR4_APSS_SPEED_BIN = 0,
1510 CPR4_APSS_CPR_FUSE_REV,
1511 CPR4_APSS_FOUNDRY_ID,
1512 CPR4_APSS_FUSE_COMBO_PARAM_COUNT,
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301513};
1514
1515/**
1516 * cpr4_parse_fuse_combo_map() - parse APSS fuse combo map data from device tree
1517 * properties of the CPR3 regulator's device node
1518 * @vreg: Pointer to the CPR3 regulator
1519 *
1520 * Return: 0 on success, errno on failure
1521 */
1522static int cpr4_parse_fuse_combo_map(struct cpr3_regulator *vreg)
1523{
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301524 struct cpr4_apss_fuses *fuse = vreg->platform_fuses;
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301525 u64 *fuse_val;
1526 int rc;
1527
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301528 fuse_val = kcalloc(CPR4_APSS_FUSE_COMBO_PARAM_COUNT,
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301529 sizeof(*fuse_val), GFP_KERNEL);
1530 if (!fuse_val)
1531 return -ENOMEM;
1532
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301533 fuse_val[CPR4_APSS_SPEED_BIN] = fuse->speed_bin;
1534 fuse_val[CPR4_APSS_CPR_FUSE_REV] = fuse->cpr_fusing_rev;
1535 fuse_val[CPR4_APSS_FOUNDRY_ID] = fuse->foundry_id;
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301536 rc = cpr3_parse_fuse_combo_map(vreg, fuse_val,
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301537 CPR4_APSS_FUSE_COMBO_PARAM_COUNT);
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301538 if (rc == -ENODEV) {
1539 cpr3_debug(vreg, "using legacy fuse combo logic, rc=%d\n",
1540 rc);
1541 rc = 0;
1542 } else if (rc < 0) {
1543 cpr3_err(vreg, "error reading fuse combo map data, rc=%d\n",
1544 rc);
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301545 } else if (vreg->fuse_combo >= CPR4_APSS_FUSE_COMBO_MAP_MAX_COUNT) {
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301546 cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
1547 vreg->fuse_combo);
1548 rc = -EINVAL;
1549 }
1550
1551 kfree(fuse_val);
1552 return rc;
1553}
1554
David Collins7370f1a2017-01-18 16:21:53 -08001555/**
1556 * cpr4_apss_init_regulator() - perform all steps necessary to initialize the
1557 * configuration data for a CPR3 regulator
1558 * @vreg: Pointer to the CPR3 regulator
1559 *
1560 * Return: 0 on success, errno on failure
1561 */
1562static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg)
1563{
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301564 struct cpr4_apss_fuses *fuse;
David Collins7370f1a2017-01-18 16:21:53 -08001565 int rc;
1566
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301567 rc = cpr4_apss_read_fuse_data(vreg);
David Collins7370f1a2017-01-18 16:21:53 -08001568 if (rc) {
1569 cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc);
1570 return rc;
1571 }
1572
1573 fuse = vreg->platform_fuses;
1574
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301575 rc = cpr4_parse_fuse_combo_map(vreg);
1576 if (rc) {
1577 cpr3_err(vreg, "error while parsing fuse combo map, rc=%d\n",
1578 rc);
1579 return rc;
1580 }
1581
David Collins7370f1a2017-01-18 16:21:53 -08001582 rc = cpr4_apss_parse_corner_data(vreg);
1583 if (rc) {
1584 cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
1585 rc);
1586 return rc;
1587 }
1588
1589 rc = cpr3_mem_acc_init(vreg);
1590 if (rc) {
1591 if (rc != -EPROBE_DEFER)
1592 cpr3_err(vreg, "unable to initialize mem-acc regulator settings, rc=%d\n",
1593 rc);
1594 return rc;
1595 }
1596
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301597 rc = cpr4_apss_calculate_open_loop_voltages(vreg);
David Collins7370f1a2017-01-18 16:21:53 -08001598 if (rc) {
1599 cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n",
1600 rc);
1601 return rc;
1602 }
1603
1604 rc = cpr3_limit_open_loop_voltages(vreg);
1605 if (rc) {
1606 cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n",
1607 rc);
1608 return rc;
1609 }
1610
1611 cpr3_open_loop_voltage_as_ceiling(vreg);
1612
1613 rc = cpr3_limit_floor_voltages(vreg);
1614 if (rc) {
1615 cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc);
1616 return rc;
1617 }
1618
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301619 rc = cpr4_apss_calculate_target_quotients(vreg);
David Collins7370f1a2017-01-18 16:21:53 -08001620 if (rc) {
1621 cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n",
1622 rc);
1623 return rc;
1624 }
1625
1626 rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false);
1627 if (rc) {
1628 cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n",
1629 rc);
1630 return rc;
1631 }
1632
1633 if (vreg->allow_core_count_adj && (vreg->max_core_count <= 0
1634 || vreg->max_core_count >
1635 MSM8953_APSS_CPR_SDELTA_CORE_COUNT)) {
1636 cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n",
1637 vreg->max_core_count);
1638 return -EINVAL;
1639 }
1640
1641 rc = cpr4_apss_parse_boost_properties(vreg);
1642 if (rc) {
1643 cpr3_err(vreg, "unable to parse boost adjustments, rc=%d\n",
1644 rc);
1645 return rc;
1646 }
1647
1648 cpr4_apss_print_settings(vreg);
1649
1650 return rc;
1651}
1652
1653/**
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +05301654 * cpr4_apss_init_aging() - perform APSS CPR4 controller specific
1655 * aging initializations
1656 * @ctrl: Pointer to the CPR3 controller
1657 *
1658 * Return: 0 on success, errno on failure
1659 */
1660static int cpr4_apss_init_aging(struct cpr3_controller *ctrl)
1661{
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301662 struct cpr4_apss_fuses *fuse = NULL;
Tirupathi Reddy53d99a02016-08-08 17:04:23 +05301663 struct cpr3_regulator *vreg = NULL;
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +05301664 u32 aging_ro_scale;
1665 int i, j, rc;
1666
1667 for (i = 0; i < ctrl->thread_count; i++) {
1668 for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
1669 if (ctrl->thread[i].vreg[j].aging_allowed) {
1670 ctrl->aging_required = true;
1671 vreg = &ctrl->thread[i].vreg[j];
1672 fuse = vreg->platform_fuses;
1673 break;
1674 }
1675 }
1676 }
1677
1678 if (!ctrl->aging_required || !fuse)
1679 return 0;
1680
1681 rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
1682 1, &aging_ro_scale);
1683 if (rc)
1684 return rc;
1685
1686 if (aging_ro_scale == 0) {
1687 cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
1688 aging_ro_scale);
1689 return -EINVAL;
1690 }
1691
1692 ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
1693 ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
1694
1695 ctrl->aging_sensor_count = 1;
1696 ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
1697 if (!ctrl->aging_sensor)
1698 return -ENOMEM;
1699
1700 ctrl->aging_sensor->sensor_id = MSM8953_APSS_AGING_SENSOR_ID;
1701 ctrl->aging_sensor->ro_scale = aging_ro_scale;
1702
1703 ctrl->aging_sensor->init_quot_diff
1704 = cpr3_convert_open_loop_voltage_fuse(0,
1705 MSM8953_APSS_AGING_INIT_QUOT_DIFF_SCALE,
1706 fuse->aging_init_quot_diff,
1707 MSM8953_APSS_AGING_INIT_QUOT_DIFF_SIZE);
1708
1709 if (ctrl->aging_sensor->init_quot_diff == 0) {
1710 /*
1711 * Initial quotient difference value '0' has a special meaning
1712 * in MSM8953 fusing scheme. Use max age margin quotient
1713 * difference to consider full aging margin of 15 mV.
1714 */
1715 ctrl->aging_sensor->init_quot_diff
1716 = MSM8953_APSS_AGING_MAX_AGE_MARGIN_QUOT;
1717 cpr3_debug(ctrl, "Init quotient diff = 0, use max age margin quotient\n");
1718 }
1719
1720 cpr3_info(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
1721 ctrl->aging_sensor->sensor_id,
1722 ctrl->aging_sensor->init_quot_diff,
1723 ctrl->aging_sensor->ro_scale);
1724
1725 return 0;
1726}
1727
1728/**
David Collins7370f1a2017-01-18 16:21:53 -08001729 * cpr4_apss_init_controller() - perform APSS CPR4 controller specific
1730 * initializations
1731 * @ctrl: Pointer to the CPR3 controller
1732 *
1733 * Return: 0 on success, errno on failure
1734 */
1735static int cpr4_apss_init_controller(struct cpr3_controller *ctrl)
1736{
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301737 int i, rc;
David Collins7370f1a2017-01-18 16:21:53 -08001738
1739 rc = cpr3_parse_common_ctrl_data(ctrl);
1740 if (rc) {
1741 if (rc != -EPROBE_DEFER)
1742 cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n",
1743 rc);
1744 return rc;
1745 }
1746
1747 rc = of_property_read_u32(ctrl->dev->of_node,
1748 "qcom,cpr-down-error-step-limit",
1749 &ctrl->down_error_step_limit);
1750 if (rc) {
1751 cpr3_err(ctrl, "error reading qcom,cpr-down-error-step-limit, rc=%d\n",
1752 rc);
1753 return rc;
1754 }
1755
1756 rc = of_property_read_u32(ctrl->dev->of_node,
1757 "qcom,cpr-up-error-step-limit",
1758 &ctrl->up_error_step_limit);
1759 if (rc) {
1760 cpr3_err(ctrl, "error reading qcom,cpr-up-error-step-limit, rc=%d\n",
1761 rc);
1762 return rc;
1763 }
1764
1765 /*
1766 * Use fixed step quotient if specified otherwise use dynamic
1767 * calculated per RO step quotient
1768 */
1769 of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed",
1770 &ctrl->step_quot_fixed);
1771 ctrl->use_dynamic_step_quot = ctrl->step_quot_fixed ? false : true;
1772
1773 ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node,
1774 "qcom,cpr-saw-use-unit-mV");
1775
1776 of_property_read_u32(ctrl->dev->of_node,
1777 "qcom,cpr-voltage-settling-time",
1778 &ctrl->voltage_settling_time);
1779
1780 ctrl->vdd_limit_regulator = devm_regulator_get(ctrl->dev, "vdd-limit");
1781 if (IS_ERR(ctrl->vdd_limit_regulator)) {
1782 rc = PTR_ERR(ctrl->vdd_limit_regulator);
1783 if (rc != -EPROBE_DEFER)
1784 cpr3_err(ctrl, "unable to request vdd-limit regulator, rc=%d\n",
1785 rc);
1786 return rc;
1787 }
1788
1789 rc = cpr3_apm_init(ctrl);
1790 if (rc) {
1791 if (rc != -EPROBE_DEFER)
1792 cpr3_err(ctrl, "unable to initialize APM settings, rc=%d\n",
1793 rc);
1794 return rc;
1795 }
1796
1797 rc = cpr4_apss_parse_temp_adj_properties(ctrl);
1798 if (rc) {
1799 cpr3_err(ctrl, "unable to parse temperature adjustment properties, rc=%d\n",
1800 rc);
1801 return rc;
1802 }
1803
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301804 switch (ctrl->soc_revision) {
1805 case MSM8953_SOC_ID:
1806 ctrl->sensor_count = MSM8953_APSS_CPR_SENSOR_COUNT;
1807 break;
1808 case SDM632_SOC_ID:
1809 ctrl->sensor_count = SDM632_APSS_CPR_SENSOR_COUNT;
1810 break;
1811 default:
1812 cpr3_err(ctrl, "unsupported soc id = %d\n", ctrl->soc_revision);
1813 return -EINVAL;
1814 }
David Collins7370f1a2017-01-18 16:21:53 -08001815
1816 /*
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301817 * MSM8953 APSS only has one thread (0) per controller so the zeroed
David Collins7370f1a2017-01-18 16:21:53 -08001818 * array does not need further modification.
1819 */
1820 ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count,
1821 sizeof(*ctrl->sensor_owner), GFP_KERNEL);
1822 if (!ctrl->sensor_owner)
1823 return -ENOMEM;
1824
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301825 /* Specify sensor ownership for SDM632 APSS CPR */
1826 if (ctrl->soc_revision == SDM632_SOC_ID) {
1827 for (i = SDM632_APSS_THREAD0_SENSOR_MIN;
1828 i <= SDM632_APSS_THREAD0_SENSOR_MAX; i++)
1829 ctrl->sensor_owner[i] = 0;
1830 for (i = SDM632_APSS_THREAD1_SENSOR_MIN;
1831 i <= SDM632_APSS_THREAD1_SENSOR_MAX; i++)
1832 ctrl->sensor_owner[i] = 1;
1833 }
1834
1835 ctrl->cpr_clock_rate = CPR4_APSS_CPR_CLOCK_RATE;
David Collins7370f1a2017-01-18 16:21:53 -08001836 ctrl->ctrl_type = CPR_CTRL_TYPE_CPR4;
1837 ctrl->supports_hw_closed_loop = true;
1838 ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node,
1839 "qcom,cpr-hw-closed-loop");
1840 return 0;
1841}
1842
Tirupathi Reddy6d51fc32018-01-23 14:11:56 +05301843#if CONFIG_PM
1844static int cpr4_apss_regulator_suspend(struct device *dev)
David Collins7370f1a2017-01-18 16:21:53 -08001845{
Tirupathi Reddy6d51fc32018-01-23 14:11:56 +05301846 struct cpr3_controller *ctrl = dev_get_drvdata(dev);
David Collins7370f1a2017-01-18 16:21:53 -08001847
1848 return cpr3_regulator_suspend(ctrl);
1849}
1850
Tirupathi Reddy6d51fc32018-01-23 14:11:56 +05301851static int cpr4_apss_regulator_resume(struct device *dev)
David Collins7370f1a2017-01-18 16:21:53 -08001852{
Tirupathi Reddy6d51fc32018-01-23 14:11:56 +05301853 struct cpr3_controller *ctrl = dev_get_drvdata(dev);
David Collins7370f1a2017-01-18 16:21:53 -08001854
1855 return cpr3_regulator_resume(ctrl);
1856}
Tirupathi Reddy6d51fc32018-01-23 14:11:56 +05301857#else
1858#define cpr4_apss_regulator_suspend NULL
1859#define cpr4_apss_regulator_resume NULL
1860#endif
1861
1862static const struct dev_pm_ops cpr4_apss_regulator_pm_ops = {
1863 .suspend = cpr4_apss_regulator_suspend,
1864 .resume = cpr4_apss_regulator_resume,
1865};
David Collins7370f1a2017-01-18 16:21:53 -08001866
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301867/* Data corresponds to the SoC revision */
1868static const struct of_device_id cpr4_regulator_match_table[] = {
1869 {
1870 .compatible = "qcom,cpr4-msm8953-apss-regulator",
1871 .data = (void *)(uintptr_t)MSM8953_SOC_ID,
1872 },
1873 {
1874 .compatible = "qcom,cpr4-sdm632-apss-regulator",
1875 .data = (void *)(uintptr_t)SDM632_SOC_ID,
1876 },
1877 {}
1878};
1879
David Collins7370f1a2017-01-18 16:21:53 -08001880static int cpr4_apss_regulator_probe(struct platform_device *pdev)
1881{
1882 struct device *dev = &pdev->dev;
1883 struct cpr3_controller *ctrl;
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301884 struct cpr3_regulator *vreg;
1885 const struct of_device_id *match;
1886 int i, j, rc, max_thread_id;
David Collins7370f1a2017-01-18 16:21:53 -08001887
1888 if (!dev->of_node) {
1889 dev_err(dev, "Device tree node is missing\n");
1890 return -EINVAL;
1891 }
1892
1893 ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
1894 if (!ctrl)
1895 return -ENOMEM;
1896
1897 ctrl->dev = dev;
1898 /* Set to false later if anything precludes CPR operation. */
1899 ctrl->cpr_allowed_hw = true;
1900
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301901 match = of_match_node(cpr4_regulator_match_table, dev->of_node);
1902 if (match)
1903 ctrl->soc_revision = (uintptr_t)match->data;
1904 else
1905 cpr3_err(ctrl, "could not find compatible string match\n");
1906
David Collins7370f1a2017-01-18 16:21:53 -08001907 rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name",
1908 &ctrl->name);
1909 if (rc) {
1910 cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n",
1911 rc);
1912 return rc;
1913 }
1914
1915 rc = cpr3_map_fuse_base(ctrl, pdev);
1916 if (rc) {
1917 cpr3_err(ctrl, "could not map fuse base address\n");
1918 return rc;
1919 }
1920
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301921 max_thread_id = 0;
1922 /* SDM632 uses 2 CPR HW threads */
1923 if (ctrl->soc_revision == SDM632_SOC_ID)
1924 max_thread_id = 1;
1925 rc = cpr3_allocate_threads(ctrl, 0, max_thread_id);
David Collins7370f1a2017-01-18 16:21:53 -08001926 if (rc) {
1927 cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n",
1928 rc);
1929 return rc;
1930 }
1931
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301932 if (ctrl->thread_count < 1) {
1933 cpr3_err(ctrl, "thread nodes are missing\n");
David Collins7370f1a2017-01-18 16:21:53 -08001934 return -EINVAL;
1935 }
1936
1937 rc = cpr4_apss_init_controller(ctrl);
1938 if (rc) {
1939 if (rc != -EPROBE_DEFER)
1940 cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n",
1941 rc);
1942 return rc;
1943 }
1944
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301945 for (i = 0; i < ctrl->thread_count; i++) {
1946 rc = cpr4_apss_init_thread(&ctrl->thread[i]);
David Collins7370f1a2017-01-18 16:21:53 -08001947 if (rc) {
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301948 cpr3_err(ctrl, "thread %u initialization failed, rc=%d\n",
1949 ctrl->thread[i].thread_id, rc);
David Collins7370f1a2017-01-18 16:21:53 -08001950 return rc;
1951 }
Tirupathi Reddy526719c2018-03-08 13:39:16 +05301952
1953 for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
1954 vreg = &ctrl->thread[i].vreg[j];
1955
1956 rc = cpr4_apss_init_regulator(vreg);
1957 if (rc) {
1958 cpr3_err(vreg, "regulator initialization failed, rc=%d\n",
1959 rc);
1960 return rc;
1961 }
1962 }
David Collins7370f1a2017-01-18 16:21:53 -08001963 }
1964
Tirupathi Reddy1bcb64f2016-03-01 16:54:40 +05301965 rc = cpr4_apss_init_aging(ctrl);
1966 if (rc) {
1967 cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
1968 rc);
1969 return rc;
1970 }
1971
David Collins7370f1a2017-01-18 16:21:53 -08001972 platform_set_drvdata(pdev, ctrl);
1973
1974 return cpr3_regulator_register(pdev, ctrl);
1975}
1976
1977static int cpr4_apss_regulator_remove(struct platform_device *pdev)
1978{
1979 struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
1980
1981 return cpr3_regulator_unregister(ctrl);
1982}
1983
David Collins7370f1a2017-01-18 16:21:53 -08001984static struct platform_driver cpr4_apss_regulator_driver = {
1985 .driver = {
1986 .name = "qcom,cpr4-apss-regulator",
1987 .of_match_table = cpr4_regulator_match_table,
1988 .owner = THIS_MODULE,
Tirupathi Reddy6d51fc32018-01-23 14:11:56 +05301989 .pm = &cpr4_apss_regulator_pm_ops,
David Collins7370f1a2017-01-18 16:21:53 -08001990 },
1991 .probe = cpr4_apss_regulator_probe,
1992 .remove = cpr4_apss_regulator_remove,
David Collins7370f1a2017-01-18 16:21:53 -08001993};
1994
1995static int cpr4_regulator_init(void)
1996{
1997 return platform_driver_register(&cpr4_apss_regulator_driver);
1998}
1999
2000static void cpr4_regulator_exit(void)
2001{
2002 platform_driver_unregister(&cpr4_apss_regulator_driver);
2003}
2004
2005MODULE_DESCRIPTION("CPR4 APSS regulator driver");
2006MODULE_LICENSE("GPL v2");
2007
2008arch_initcall(cpr4_regulator_init);
2009module_exit(cpr4_regulator_exit);