| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. | 
|  | 3 | * | 
|  | 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 | /* | 
|  | 15 | * This file contains utility functions to be used by platform specific CPR3 | 
|  | 16 | * regulator drivers. | 
|  | 17 | */ | 
|  | 18 |  | 
|  | 19 | #define pr_fmt(fmt) "%s: " fmt, __func__ | 
|  | 20 |  | 
|  | 21 | #include <linux/cpumask.h> | 
|  | 22 | #include <linux/device.h> | 
|  | 23 | #include <linux/io.h> | 
|  | 24 | #include <linux/kernel.h> | 
|  | 25 | #include <linux/of.h> | 
|  | 26 | #include <linux/platform_device.h> | 
|  | 27 | #include <linux/slab.h> | 
|  | 28 | #include <linux/types.h> | 
|  | 29 |  | 
|  | 30 | #include "cpr3-regulator.h" | 
|  | 31 |  | 
|  | 32 | #define BYTES_PER_FUSE_ROW		8 | 
|  | 33 | #define MAX_FUSE_ROW_BIT		63 | 
|  | 34 |  | 
|  | 35 | #define CPR3_CONSECUTIVE_UP_DOWN_MIN	0 | 
|  | 36 | #define CPR3_CONSECUTIVE_UP_DOWN_MAX	15 | 
|  | 37 | #define CPR3_UP_DOWN_THRESHOLD_MIN	0 | 
|  | 38 | #define CPR3_UP_DOWN_THRESHOLD_MAX	31 | 
|  | 39 | #define CPR3_STEP_QUOT_MIN		0 | 
|  | 40 | #define CPR3_STEP_QUOT_MAX		63 | 
|  | 41 | #define CPR3_IDLE_CLOCKS_MIN		0 | 
|  | 42 | #define CPR3_IDLE_CLOCKS_MAX		31 | 
|  | 43 |  | 
|  | 44 | /* This constant has units of uV/mV so 1000 corresponds to 100%. */ | 
|  | 45 | #define CPR3_AGING_DERATE_UNITY		1000 | 
|  | 46 |  | 
|  | 47 | /** | 
|  | 48 | * cpr3_allocate_regulators() - allocate and initialize CPR3 regulators for a | 
|  | 49 | *		given thread based upon device tree data | 
|  | 50 | * @thread:		Pointer to the CPR3 thread | 
|  | 51 | * | 
|  | 52 | * This function allocates the thread->vreg array based upon the number of | 
|  | 53 | * device tree regulator subnodes.  It also initializes generic elements of each | 
|  | 54 | * regulator struct such as name, of_node, and thread. | 
|  | 55 | * | 
|  | 56 | * Return: 0 on success, errno on failure | 
|  | 57 | */ | 
|  | 58 | static int cpr3_allocate_regulators(struct cpr3_thread *thread) | 
|  | 59 | { | 
|  | 60 | struct device_node *node; | 
|  | 61 | int i, rc; | 
|  | 62 |  | 
|  | 63 | thread->vreg_count = 0; | 
|  | 64 |  | 
|  | 65 | for_each_available_child_of_node(thread->of_node, node) { | 
|  | 66 | thread->vreg_count++; | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | thread->vreg = devm_kcalloc(thread->ctrl->dev, thread->vreg_count, | 
|  | 70 | sizeof(*thread->vreg), GFP_KERNEL); | 
|  | 71 | if (!thread->vreg) | 
|  | 72 | return -ENOMEM; | 
|  | 73 |  | 
|  | 74 | i = 0; | 
|  | 75 | for_each_available_child_of_node(thread->of_node, node) { | 
|  | 76 | thread->vreg[i].of_node = node; | 
|  | 77 | thread->vreg[i].thread = thread; | 
|  | 78 |  | 
|  | 79 | rc = of_property_read_string(node, "regulator-name", | 
|  | 80 | &thread->vreg[i].name); | 
|  | 81 | if (rc) { | 
|  | 82 | dev_err(thread->ctrl->dev, "could not find regulator name, rc=%d\n", | 
|  | 83 | rc); | 
|  | 84 | return rc; | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | i++; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | return 0; | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | /** | 
|  | 94 | * cpr3_allocate_threads() - allocate and initialize CPR3 threads for a given | 
|  | 95 | *			     controller based upon device tree data | 
|  | 96 | * @ctrl:		Pointer to the CPR3 controller | 
|  | 97 | * @min_thread_id:	Minimum allowed hardware thread ID for this controller | 
|  | 98 | * @max_thread_id:	Maximum allowed hardware thread ID for this controller | 
|  | 99 | * | 
|  | 100 | * This function allocates the ctrl->thread array based upon the number of | 
|  | 101 | * device tree thread subnodes.  It also initializes generic elements of each | 
|  | 102 | * thread struct such as thread_id, of_node, ctrl, and vreg array. | 
|  | 103 | * | 
|  | 104 | * Return: 0 on success, errno on failure | 
|  | 105 | */ | 
|  | 106 | int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, | 
|  | 107 | u32 max_thread_id) | 
|  | 108 | { | 
|  | 109 | struct device *dev = ctrl->dev; | 
|  | 110 | struct device_node *thread_node; | 
|  | 111 | int i, j, rc; | 
|  | 112 |  | 
|  | 113 | ctrl->thread_count = 0; | 
|  | 114 |  | 
|  | 115 | for_each_available_child_of_node(dev->of_node, thread_node) { | 
|  | 116 | ctrl->thread_count++; | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | ctrl->thread = devm_kcalloc(dev, ctrl->thread_count, | 
|  | 120 | sizeof(*ctrl->thread), GFP_KERNEL); | 
|  | 121 | if (!ctrl->thread) | 
|  | 122 | return -ENOMEM; | 
|  | 123 |  | 
|  | 124 | i = 0; | 
|  | 125 | for_each_available_child_of_node(dev->of_node, thread_node) { | 
|  | 126 | ctrl->thread[i].of_node = thread_node; | 
|  | 127 | ctrl->thread[i].ctrl = ctrl; | 
|  | 128 |  | 
|  | 129 | rc = of_property_read_u32(thread_node, "qcom,cpr-thread-id", | 
|  | 130 | &ctrl->thread[i].thread_id); | 
|  | 131 | if (rc) { | 
|  | 132 | dev_err(dev, "could not read DT property qcom,cpr-thread-id, rc=%d\n", | 
|  | 133 | rc); | 
|  | 134 | return rc; | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | if (ctrl->thread[i].thread_id < min_thread_id || | 
|  | 138 | ctrl->thread[i].thread_id > max_thread_id) { | 
|  | 139 | dev_err(dev, "invalid thread id = %u; not within [%u, %u]\n", | 
|  | 140 | ctrl->thread[i].thread_id, min_thread_id, | 
|  | 141 | max_thread_id); | 
|  | 142 | return -EINVAL; | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | /* Verify that the thread ID is unique for all child nodes. */ | 
|  | 146 | for (j = 0; j < i; j++) { | 
|  | 147 | if (ctrl->thread[j].thread_id | 
|  | 148 | == ctrl->thread[i].thread_id) { | 
|  | 149 | dev_err(dev, "duplicate thread id = %u found\n", | 
|  | 150 | ctrl->thread[i].thread_id); | 
|  | 151 | return -EINVAL; | 
|  | 152 | } | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | rc = cpr3_allocate_regulators(&ctrl->thread[i]); | 
|  | 156 | if (rc) | 
|  | 157 | return rc; | 
|  | 158 |  | 
|  | 159 | i++; | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | return 0; | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | /** | 
|  | 166 | * cpr3_map_fuse_base() - ioremap the base address of the fuse region | 
|  | 167 | * @ctrl:	Pointer to the CPR3 controller | 
|  | 168 | * @pdev:	Platform device pointer for the CPR3 controller | 
|  | 169 | * | 
|  | 170 | * Return: 0 on success, errno on failure | 
|  | 171 | */ | 
|  | 172 | int cpr3_map_fuse_base(struct cpr3_controller *ctrl, | 
|  | 173 | struct platform_device *pdev) | 
|  | 174 | { | 
|  | 175 | struct resource *res; | 
|  | 176 |  | 
|  | 177 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse_base"); | 
|  | 178 | if (!res || !res->start) { | 
|  | 179 | dev_err(&pdev->dev, "fuse base address is missing\n"); | 
|  | 180 | return -ENXIO; | 
|  | 181 | } | 
|  | 182 |  | 
|  | 183 | ctrl->fuse_base = devm_ioremap(&pdev->dev, res->start, | 
|  | 184 | resource_size(res)); | 
|  | 185 |  | 
|  | 186 | return 0; | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 | /** | 
|  | 190 | * cpr3_read_fuse_param() - reads a CPR3 fuse parameter out of eFuses | 
|  | 191 | * @fuse_base_addr:	Virtual memory address of the eFuse base address | 
|  | 192 | * @param:		Null terminated array of fuse param segments to read | 
|  | 193 | *			from | 
|  | 194 | * @param_value:	Output with value read from the eFuses | 
|  | 195 | * | 
|  | 196 | * This function reads from each of the parameter segments listed in the param | 
|  | 197 | * array and concatenates their values together.  Reading stops when an element | 
|  | 198 | * is reached which has all 0 struct values.  The total number of bits specified | 
|  | 199 | * for the fuse parameter across all segments must be less than or equal to 64. | 
|  | 200 | * | 
|  | 201 | * Return: 0 on success, errno on failure | 
|  | 202 | */ | 
|  | 203 | int cpr3_read_fuse_param(void __iomem *fuse_base_addr, | 
|  | 204 | const struct cpr3_fuse_param *param, u64 *param_value) | 
|  | 205 | { | 
|  | 206 | u64 fuse_val, val; | 
|  | 207 | int bits; | 
|  | 208 | int bits_total = 0; | 
|  | 209 |  | 
|  | 210 | *param_value = 0; | 
|  | 211 |  | 
|  | 212 | while (param->row || param->bit_start || param->bit_end) { | 
|  | 213 | if (param->bit_start > param->bit_end | 
|  | 214 | || param->bit_end > MAX_FUSE_ROW_BIT) { | 
|  | 215 | pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n", | 
|  | 216 | param->row, param->bit_start, param->bit_end); | 
|  | 217 | return -EINVAL; | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | bits = param->bit_end - param->bit_start + 1; | 
|  | 221 | if (bits_total + bits > 64) { | 
|  | 222 | pr_err("Invalid fuse parameter segments; total bits = %d\n", | 
|  | 223 | bits_total + bits); | 
|  | 224 | return -EINVAL; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | fuse_val = readq_relaxed(fuse_base_addr | 
|  | 228 | + param->row * BYTES_PER_FUSE_ROW); | 
|  | 229 | val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1); | 
|  | 230 | *param_value |= val << bits_total; | 
|  | 231 | bits_total += bits; | 
|  | 232 |  | 
|  | 233 | param++; | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | return 0; | 
|  | 237 | } | 
|  | 238 |  | 
|  | 239 | /** | 
|  | 240 | * cpr3_convert_open_loop_voltage_fuse() - converts an open loop voltage fuse | 
|  | 241 | *		value into an absolute voltage with units of microvolts | 
|  | 242 | * @ref_volt:		Reference voltage in microvolts | 
|  | 243 | * @step_volt:		The step size in microvolts of the fuse LSB | 
|  | 244 | * @fuse:		Open loop voltage fuse value | 
|  | 245 | * @fuse_len:		The bit length of the fuse value | 
|  | 246 | * | 
|  | 247 | * The MSB of the fuse parameter corresponds to a sign bit.  If it is set, then | 
|  | 248 | * the lower bits correspond to the number of steps to go down from the | 
|  | 249 | * reference voltage.  If it is not set, then the lower bits correspond to the | 
|  | 250 | * number of steps to go up from the reference voltage. | 
|  | 251 | */ | 
|  | 252 | int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, | 
|  | 253 | int fuse_len) | 
|  | 254 | { | 
|  | 255 | int sign, steps; | 
|  | 256 |  | 
|  | 257 | sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1; | 
|  | 258 | steps = fuse & ((1 << (fuse_len - 1)) - 1); | 
|  | 259 |  | 
|  | 260 | return ref_volt + sign * steps * step_volt; | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | /** | 
|  | 264 | * cpr3_interpolate() - performs linear interpolation | 
|  | 265 | * @x1		Lower known x value | 
|  | 266 | * @y1		Lower known y value | 
|  | 267 | * @x2		Upper known x value | 
|  | 268 | * @y2		Upper known y value | 
|  | 269 | * @x		Intermediate x value | 
|  | 270 | * | 
|  | 271 | * Returns y where (x, y) falls on the line between (x1, y1) and (x2, y2). | 
|  | 272 | * It is required that x1 < x2, y1 <= y2, and x1 <= x <= x2.  If these | 
|  | 273 | * conditions are not met, then y2 will be returned. | 
|  | 274 | */ | 
|  | 275 | u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) | 
|  | 276 | { | 
|  | 277 | u64 temp; | 
|  | 278 |  | 
|  | 279 | if (x1 >= x2 || y1 > y2 || x1 > x || x > x2) | 
|  | 280 | return y2; | 
|  | 281 |  | 
|  | 282 | temp = (x2 - x) * (y2 - y1); | 
|  | 283 | do_div(temp, (u32)(x2 - x1)); | 
|  | 284 |  | 
|  | 285 | return y2 - temp; | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | /** | 
|  | 289 | * cpr3_parse_array_property() - fill an array from a portion of the values | 
|  | 290 | *		specified for a device tree property | 
|  | 291 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 292 | * @prop_name:		The name of the device tree property to read from | 
|  | 293 | * @tuple_size:		The number of elements in each tuple | 
|  | 294 | * @out:		Output data array which must be of size tuple_size | 
|  | 295 | * | 
|  | 296 | * cpr3_parse_common_corner_data() must be called for vreg before this function | 
|  | 297 | * is called so that fuse combo and speed bin size elements are initialized. | 
|  | 298 | * | 
|  | 299 | * Three formats are supported for the device tree property: | 
|  | 300 | * 1. Length == tuple_size | 
|  | 301 | *	(reading begins at index 0) | 
|  | 302 | * 2. Length == tuple_size * vreg->fuse_combos_supported | 
|  | 303 | *	(reading begins at index tuple_size * vreg->fuse_combo) | 
|  | 304 | * 3. Length == tuple_size * vreg->speed_bins_supported | 
|  | 305 | *	(reading begins at index tuple_size * vreg->speed_bin_fuse) | 
|  | 306 | * | 
|  | 307 | * All other property lengths are treated as errors. | 
|  | 308 | * | 
|  | 309 | * Return: 0 on success, errno on failure | 
|  | 310 | */ | 
|  | 311 | int cpr3_parse_array_property(struct cpr3_regulator *vreg, | 
|  | 312 | const char *prop_name, int tuple_size, u32 *out) | 
|  | 313 | { | 
|  | 314 | struct device_node *node = vreg->of_node; | 
|  | 315 | int len = 0; | 
|  | 316 | int i, offset, rc; | 
|  | 317 |  | 
|  | 318 | if (!of_find_property(node, prop_name, &len)) { | 
|  | 319 | cpr3_err(vreg, "property %s is missing\n", prop_name); | 
|  | 320 | return -EINVAL; | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | if (len == tuple_size * sizeof(u32)) { | 
|  | 324 | offset = 0; | 
|  | 325 | } else if (len == tuple_size * vreg->fuse_combos_supported | 
|  | 326 | * sizeof(u32)) { | 
|  | 327 | offset = tuple_size * vreg->fuse_combo; | 
|  | 328 | } else if (vreg->speed_bins_supported > 0 && | 
|  | 329 | len == tuple_size * vreg->speed_bins_supported * sizeof(u32)) { | 
|  | 330 | offset = tuple_size * vreg->speed_bin_fuse; | 
|  | 331 | } else { | 
|  | 332 | if (vreg->speed_bins_supported > 0) | 
|  | 333 | cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", | 
|  | 334 | prop_name, len, | 
|  | 335 | tuple_size * sizeof(u32), | 
|  | 336 | tuple_size * vreg->speed_bins_supported | 
|  | 337 | * sizeof(u32), | 
|  | 338 | tuple_size * vreg->fuse_combos_supported | 
|  | 339 | * sizeof(u32)); | 
|  | 340 | else | 
|  | 341 | cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", | 
|  | 342 | prop_name, len, | 
|  | 343 | tuple_size * sizeof(u32), | 
|  | 344 | tuple_size * vreg->fuse_combos_supported | 
|  | 345 | * sizeof(u32)); | 
|  | 346 | return -EINVAL; | 
|  | 347 | } | 
|  | 348 |  | 
|  | 349 | for (i = 0; i < tuple_size; i++) { | 
|  | 350 | rc = of_property_read_u32_index(node, prop_name, offset + i, | 
|  | 351 | &out[i]); | 
|  | 352 | if (rc) { | 
|  | 353 | cpr3_err(vreg, "error reading property %s, rc=%d\n", | 
|  | 354 | prop_name, rc); | 
|  | 355 | return rc; | 
|  | 356 | } | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | return 0; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | /** | 
|  | 363 | * cpr3_parse_corner_array_property() - fill a per-corner array from a portion | 
|  | 364 | *		of the values specified for a device tree property | 
|  | 365 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 366 | * @prop_name:		The name of the device tree property to read from | 
|  | 367 | * @tuple_size:		The number of elements in each per-corner tuple | 
|  | 368 | * @out:		Output data array which must be of size: | 
|  | 369 | *			tuple_size * vreg->corner_count | 
|  | 370 | * | 
|  | 371 | * cpr3_parse_common_corner_data() must be called for vreg before this function | 
|  | 372 | * is called so that fuse combo and speed bin size elements are initialized. | 
|  | 373 | * | 
|  | 374 | * Three formats are supported for the device tree property: | 
|  | 375 | * 1. Length == tuple_size * vreg->corner_count | 
|  | 376 | *	(reading begins at index 0) | 
|  | 377 | * 2. Length == tuple_size * vreg->fuse_combo_corner_sum | 
|  | 378 | *	(reading begins at index tuple_size * vreg->fuse_combo_offset) | 
|  | 379 | * 3. Length == tuple_size * vreg->speed_bin_corner_sum | 
|  | 380 | *	(reading begins at index tuple_size * vreg->speed_bin_offset) | 
|  | 381 | * | 
|  | 382 | * All other property lengths are treated as errors. | 
|  | 383 | * | 
|  | 384 | * Return: 0 on success, errno on failure | 
|  | 385 | */ | 
|  | 386 | int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, | 
|  | 387 | const char *prop_name, int tuple_size, u32 *out) | 
|  | 388 | { | 
|  | 389 | struct device_node *node = vreg->of_node; | 
|  | 390 | int len = 0; | 
|  | 391 | int i, offset, rc; | 
|  | 392 |  | 
|  | 393 | if (!of_find_property(node, prop_name, &len)) { | 
|  | 394 | cpr3_err(vreg, "property %s is missing\n", prop_name); | 
|  | 395 | return -EINVAL; | 
|  | 396 | } | 
|  | 397 |  | 
|  | 398 | if (len == tuple_size * vreg->corner_count * sizeof(u32)) { | 
|  | 399 | offset = 0; | 
|  | 400 | } else if (len == tuple_size * vreg->fuse_combo_corner_sum | 
|  | 401 | * sizeof(u32)) { | 
|  | 402 | offset = tuple_size * vreg->fuse_combo_offset; | 
|  | 403 | } else if (vreg->speed_bin_corner_sum > 0 && | 
|  | 404 | len == tuple_size * vreg->speed_bin_corner_sum * sizeof(u32)) { | 
|  | 405 | offset = tuple_size * vreg->speed_bin_offset; | 
|  | 406 | } else { | 
|  | 407 | if (vreg->speed_bin_corner_sum > 0) | 
|  | 408 | cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", | 
|  | 409 | prop_name, len, | 
|  | 410 | tuple_size * vreg->corner_count * sizeof(u32), | 
|  | 411 | tuple_size * vreg->speed_bin_corner_sum | 
|  | 412 | * sizeof(u32), | 
|  | 413 | tuple_size * vreg->fuse_combo_corner_sum | 
|  | 414 | * sizeof(u32)); | 
|  | 415 | else | 
|  | 416 | cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", | 
|  | 417 | prop_name, len, | 
|  | 418 | tuple_size * vreg->corner_count * sizeof(u32), | 
|  | 419 | tuple_size * vreg->fuse_combo_corner_sum | 
|  | 420 | * sizeof(u32)); | 
|  | 421 | return -EINVAL; | 
|  | 422 | } | 
|  | 423 |  | 
|  | 424 | for (i = 0; i < tuple_size * vreg->corner_count; i++) { | 
|  | 425 | rc = of_property_read_u32_index(node, prop_name, offset + i, | 
|  | 426 | &out[i]); | 
|  | 427 | if (rc) { | 
|  | 428 | cpr3_err(vreg, "error reading property %s, rc=%d\n", | 
|  | 429 | prop_name, rc); | 
|  | 430 | return rc; | 
|  | 431 | } | 
|  | 432 | } | 
|  | 433 |  | 
|  | 434 | return 0; | 
|  | 435 | } | 
|  | 436 |  | 
|  | 437 | /** | 
|  | 438 | * cpr3_parse_corner_band_array_property() - fill a per-corner band array | 
|  | 439 | *		from a portion of the values specified for a device tree | 
|  | 440 | *		property | 
|  | 441 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 442 | * @prop_name:		The name of the device tree property to read from | 
|  | 443 | * @tuple_size:		The number of elements in each per-corner band tuple | 
|  | 444 | * @out:		Output data array which must be of size: | 
|  | 445 | *			tuple_size * vreg->corner_band_count | 
|  | 446 | * | 
|  | 447 | * cpr3_parse_common_corner_data() must be called for vreg before this function | 
|  | 448 | * is called so that fuse combo and speed bin size elements are initialized. | 
|  | 449 | * In addition, corner band fuse combo and speed bin sum and offset elements | 
|  | 450 | * must be initialized prior to executing this function. | 
|  | 451 | * | 
|  | 452 | * Three formats are supported for the device tree property: | 
|  | 453 | * 1. Length == tuple_size * vreg->corner_band_count | 
|  | 454 | *	(reading begins at index 0) | 
|  | 455 | * 2. Length == tuple_size * vreg->fuse_combo_corner_band_sum | 
|  | 456 | *	(reading begins at index tuple_size * | 
|  | 457 | *		vreg->fuse_combo_corner_band_offset) | 
|  | 458 | * 3. Length == tuple_size * vreg->speed_bin_corner_band_sum | 
|  | 459 | *	(reading begins at index tuple_size * | 
|  | 460 | *		vreg->speed_bin_corner_band_offset) | 
|  | 461 | * | 
|  | 462 | * All other property lengths are treated as errors. | 
|  | 463 | * | 
|  | 464 | * Return: 0 on success, errno on failure | 
|  | 465 | */ | 
|  | 466 | int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, | 
|  | 467 | const char *prop_name, int tuple_size, u32 *out) | 
|  | 468 | { | 
|  | 469 | struct device_node *node = vreg->of_node; | 
|  | 470 | int len = 0; | 
|  | 471 | int i, offset, rc; | 
|  | 472 |  | 
|  | 473 | if (!of_find_property(node, prop_name, &len)) { | 
|  | 474 | cpr3_err(vreg, "property %s is missing\n", prop_name); | 
|  | 475 | return -EINVAL; | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | if (len == tuple_size * vreg->corner_band_count * sizeof(u32)) { | 
|  | 479 | offset = 0; | 
|  | 480 | } else if (len == tuple_size * vreg->fuse_combo_corner_band_sum | 
|  | 481 | * sizeof(u32)) { | 
|  | 482 | offset = tuple_size * vreg->fuse_combo_corner_band_offset; | 
|  | 483 | } else if (vreg->speed_bin_corner_band_sum > 0 && | 
|  | 484 | len == tuple_size * vreg->speed_bin_corner_band_sum * | 
|  | 485 | sizeof(u32)) { | 
|  | 486 | offset = tuple_size * vreg->speed_bin_corner_band_offset; | 
|  | 487 | } else { | 
|  | 488 | if (vreg->speed_bin_corner_band_sum > 0) | 
|  | 489 | cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", | 
|  | 490 | prop_name, len, | 
|  | 491 | tuple_size * vreg->corner_band_count * | 
|  | 492 | sizeof(u32), | 
|  | 493 | tuple_size * vreg->speed_bin_corner_band_sum | 
|  | 494 | * sizeof(u32), | 
|  | 495 | tuple_size * vreg->fuse_combo_corner_band_sum | 
|  | 496 | * sizeof(u32)); | 
|  | 497 | else | 
|  | 498 | cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", | 
|  | 499 | prop_name, len, | 
|  | 500 | tuple_size * vreg->corner_band_count * | 
|  | 501 | sizeof(u32), | 
|  | 502 | tuple_size * vreg->fuse_combo_corner_band_sum | 
|  | 503 | * sizeof(u32)); | 
|  | 504 | return -EINVAL; | 
|  | 505 | } | 
|  | 506 |  | 
|  | 507 | for (i = 0; i < tuple_size * vreg->corner_band_count; i++) { | 
|  | 508 | rc = of_property_read_u32_index(node, prop_name, offset + i, | 
|  | 509 | &out[i]); | 
|  | 510 | if (rc) { | 
|  | 511 | cpr3_err(vreg, "error reading property %s, rc=%d\n", | 
|  | 512 | prop_name, rc); | 
|  | 513 | return rc; | 
|  | 514 | } | 
|  | 515 | } | 
|  | 516 |  | 
|  | 517 | return 0; | 
|  | 518 | } | 
|  | 519 |  | 
|  | 520 | /** | 
|  | 521 | * cpr3_parse_common_corner_data() - parse common CPR3 properties relating to | 
|  | 522 | *		the corners supported by a CPR3 regulator from device tree | 
|  | 523 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 524 | * | 
|  | 525 | * This function reads, validates, and utilizes the following device tree | 
|  | 526 | * properties: qcom,cpr-fuse-corners, qcom,cpr-fuse-combos, qcom,cpr-speed-bins, | 
|  | 527 | * qcom,cpr-speed-bin-corners, qcom,cpr-corners, qcom,cpr-voltage-ceiling, | 
|  | 528 | * qcom,cpr-voltage-floor, qcom,corner-frequencies, | 
|  | 529 | * and qcom,cpr-corner-fmax-map. | 
|  | 530 | * | 
|  | 531 | * It initializes these CPR3 regulator elements: corner, corner_count, | 
|  | 532 | * fuse_combos_supported, fuse_corner_map, and speed_bins_supported.  It | 
|  | 533 | * initializes these elements for each corner: ceiling_volt, floor_volt, | 
|  | 534 | * proc_freq, and cpr_fuse_corner. | 
|  | 535 | * | 
|  | 536 | * It requires that the following CPR3 regulator elements be initialized before | 
|  | 537 | * being called: fuse_corner_count, fuse_combo, and speed_bin_fuse. | 
|  | 538 | * | 
|  | 539 | * Return: 0 on success, errno on failure | 
|  | 540 | */ | 
|  | 541 | int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) | 
|  | 542 | { | 
|  | 543 | struct device_node *node = vreg->of_node; | 
|  | 544 | struct cpr3_controller *ctrl = vreg->thread->ctrl; | 
|  | 545 | u32 max_fuse_combos, fuse_corners, aging_allowed = 0; | 
|  | 546 | u32 max_speed_bins = 0; | 
|  | 547 | u32 *combo_corners; | 
|  | 548 | u32 *speed_bin_corners; | 
|  | 549 | u32 *temp; | 
|  | 550 | int i, j, rc; | 
|  | 551 |  | 
|  | 552 | rc = of_property_read_u32(node, "qcom,cpr-fuse-corners", &fuse_corners); | 
|  | 553 | if (rc) { | 
|  | 554 | cpr3_err(vreg, "error reading property qcom,cpr-fuse-corners, rc=%d\n", | 
|  | 555 | rc); | 
|  | 556 | return rc; | 
|  | 557 | } | 
|  | 558 |  | 
|  | 559 | if (vreg->fuse_corner_count != fuse_corners) { | 
|  | 560 | cpr3_err(vreg, "device tree config supports %d fuse corners but the hardware has %d fuse corners\n", | 
|  | 561 | fuse_corners, vreg->fuse_corner_count); | 
|  | 562 | return -EINVAL; | 
|  | 563 | } | 
|  | 564 |  | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 565 | /* | 
| Tirupathi Reddy | 53d99a0 | 2016-08-08 17:04:23 +0530 | [diff] [blame] | 566 | * Check if CPR3 regulator's fuse_combos_supported element is already | 
|  | 567 | * populated by fuse-combo-map logic. If not populated, then parse the | 
|  | 568 | * qcom,cpr-fuse-combos property. | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 569 | */ | 
| Tirupathi Reddy | 53d99a0 | 2016-08-08 17:04:23 +0530 | [diff] [blame] | 570 | if (vreg->fuse_combos_supported) | 
|  | 571 | max_fuse_combos = vreg->fuse_combos_supported; | 
|  | 572 | else { | 
|  | 573 | rc = of_property_read_u32(node, "qcom,cpr-fuse-combos", | 
|  | 574 | &max_fuse_combos); | 
|  | 575 | if (rc) { | 
|  | 576 | cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n", | 
|  | 577 | rc); | 
|  | 578 | return rc; | 
|  | 579 | } | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 580 |  | 
| Tirupathi Reddy | 53d99a0 | 2016-08-08 17:04:23 +0530 | [diff] [blame] | 581 | /* | 
|  | 582 | * Sanity check against arbitrarily large value to avoid | 
|  | 583 | * excessive memory allocation. | 
|  | 584 | */ | 
|  | 585 | if (max_fuse_combos > 100 || max_fuse_combos == 0) { | 
|  | 586 | cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n", | 
|  | 587 | max_fuse_combos); | 
|  | 588 | return -EINVAL; | 
|  | 589 | } | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 590 |  | 
| Tirupathi Reddy | 53d99a0 | 2016-08-08 17:04:23 +0530 | [diff] [blame] | 591 | if (vreg->fuse_combo >= max_fuse_combos) { | 
|  | 592 | cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n", | 
|  | 593 | max_fuse_combos - 1, vreg->fuse_combo); | 
|  | 594 | WARN_ON(1); | 
|  | 595 | return -EINVAL; | 
|  | 596 | } | 
|  | 597 |  | 
|  | 598 | vreg->fuse_combos_supported = max_fuse_combos; | 
|  | 599 | } | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 600 |  | 
|  | 601 | of_property_read_u32(node, "qcom,cpr-speed-bins", &max_speed_bins); | 
|  | 602 |  | 
|  | 603 | /* | 
|  | 604 | * Sanity check against arbitrarily large value to avoid excessive | 
|  | 605 | * memory allocation. | 
|  | 606 | */ | 
|  | 607 | if (max_speed_bins > 100) { | 
|  | 608 | cpr3_err(vreg, "qcom,cpr-speed-bins is invalid: %u\n", | 
|  | 609 | max_speed_bins); | 
|  | 610 | return -EINVAL; | 
|  | 611 | } | 
|  | 612 |  | 
|  | 613 | if (max_speed_bins && vreg->speed_bin_fuse >= max_speed_bins) { | 
|  | 614 | cpr3_err(vreg, "device tree config supports speed bins 0-%u but the hardware has speed bin %d\n", | 
|  | 615 | max_speed_bins - 1, vreg->speed_bin_fuse); | 
|  | 616 | BUG(); | 
|  | 617 | return -EINVAL; | 
|  | 618 | } | 
|  | 619 |  | 
|  | 620 | vreg->speed_bins_supported = max_speed_bins; | 
|  | 621 |  | 
|  | 622 | combo_corners = kcalloc(vreg->fuse_combos_supported, | 
|  | 623 | sizeof(*combo_corners), GFP_KERNEL); | 
|  | 624 | if (!combo_corners) | 
|  | 625 | return -ENOMEM; | 
|  | 626 |  | 
|  | 627 | rc = of_property_read_u32_array(node, "qcom,cpr-corners", combo_corners, | 
|  | 628 | vreg->fuse_combos_supported); | 
|  | 629 | if (rc == -EOVERFLOW) { | 
|  | 630 | /* Single value case */ | 
|  | 631 | rc = of_property_read_u32(node, "qcom,cpr-corners", | 
|  | 632 | combo_corners); | 
|  | 633 | for (i = 1; i < vreg->fuse_combos_supported; i++) | 
|  | 634 | combo_corners[i] = combo_corners[0]; | 
|  | 635 | } | 
|  | 636 | if (rc) { | 
|  | 637 | cpr3_err(vreg, "error reading property qcom,cpr-corners, rc=%d\n", | 
|  | 638 | rc); | 
|  | 639 | kfree(combo_corners); | 
|  | 640 | return rc; | 
|  | 641 | } | 
|  | 642 |  | 
|  | 643 | vreg->fuse_combo_offset = 0; | 
|  | 644 | vreg->fuse_combo_corner_sum = 0; | 
|  | 645 | for (i = 0; i < vreg->fuse_combos_supported; i++) { | 
|  | 646 | vreg->fuse_combo_corner_sum += combo_corners[i]; | 
|  | 647 | if (i < vreg->fuse_combo) | 
|  | 648 | vreg->fuse_combo_offset += combo_corners[i]; | 
|  | 649 | } | 
|  | 650 |  | 
|  | 651 | vreg->corner_count = combo_corners[vreg->fuse_combo]; | 
|  | 652 |  | 
|  | 653 | kfree(combo_corners); | 
|  | 654 |  | 
|  | 655 | vreg->speed_bin_offset = 0; | 
|  | 656 | vreg->speed_bin_corner_sum = 0; | 
|  | 657 | if (vreg->speed_bins_supported > 0) { | 
|  | 658 | speed_bin_corners = kcalloc(vreg->speed_bins_supported, | 
|  | 659 | sizeof(*speed_bin_corners), GFP_KERNEL); | 
|  | 660 | if (!speed_bin_corners) | 
|  | 661 | return -ENOMEM; | 
|  | 662 |  | 
|  | 663 | rc = of_property_read_u32_array(node, | 
|  | 664 | "qcom,cpr-speed-bin-corners", speed_bin_corners, | 
|  | 665 | vreg->speed_bins_supported); | 
|  | 666 | if (rc) { | 
|  | 667 | cpr3_err(vreg, "error reading property qcom,cpr-speed-bin-corners, rc=%d\n", | 
|  | 668 | rc); | 
|  | 669 | kfree(speed_bin_corners); | 
|  | 670 | return rc; | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | for (i = 0; i < vreg->speed_bins_supported; i++) { | 
|  | 674 | vreg->speed_bin_corner_sum += speed_bin_corners[i]; | 
|  | 675 | if (i < vreg->speed_bin_fuse) | 
|  | 676 | vreg->speed_bin_offset += speed_bin_corners[i]; | 
|  | 677 | } | 
|  | 678 |  | 
|  | 679 | if (speed_bin_corners[vreg->speed_bin_fuse] | 
|  | 680 | != vreg->corner_count) { | 
|  | 681 | cpr3_err(vreg, "qcom,cpr-corners and qcom,cpr-speed-bin-corners conflict on number of corners: %d vs %u\n", | 
|  | 682 | vreg->corner_count, | 
|  | 683 | speed_bin_corners[vreg->speed_bin_fuse]); | 
|  | 684 | kfree(speed_bin_corners); | 
|  | 685 | return -EINVAL; | 
|  | 686 | } | 
|  | 687 |  | 
|  | 688 | kfree(speed_bin_corners); | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | /* | 
|  | 692 | * For CPRh compliant controllers two additional corners are | 
|  | 693 | * allocated to correspond to the APM crossover voltage and the MEM ACC | 
|  | 694 | * crossover voltage. | 
|  | 695 | */ | 
|  | 696 | vreg->corner = devm_kcalloc(ctrl->dev, ctrl->ctrl_type == | 
|  | 697 | CPR_CTRL_TYPE_CPRH ? | 
|  | 698 | vreg->corner_count + 2 : | 
|  | 699 | vreg->corner_count, | 
|  | 700 | sizeof(*vreg->corner), GFP_KERNEL); | 
|  | 701 | temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); | 
|  | 702 | if (!vreg->corner || !temp) | 
|  | 703 | return -ENOMEM; | 
|  | 704 |  | 
|  | 705 | rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-ceiling", | 
|  | 706 | 1, temp); | 
|  | 707 | if (rc) | 
|  | 708 | goto free_temp; | 
|  | 709 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 710 | vreg->corner[i].ceiling_volt | 
|  | 711 | = CPR3_ROUND(temp[i], ctrl->step_volt); | 
|  | 712 | vreg->corner[i].abs_ceiling_volt = vreg->corner[i].ceiling_volt; | 
|  | 713 | } | 
|  | 714 |  | 
|  | 715 | rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-floor", | 
|  | 716 | 1, temp); | 
|  | 717 | if (rc) | 
|  | 718 | goto free_temp; | 
|  | 719 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 720 | vreg->corner[i].floor_volt | 
|  | 721 | = CPR3_ROUND(temp[i], ctrl->step_volt); | 
|  | 722 |  | 
|  | 723 | /* Validate ceiling and floor values */ | 
|  | 724 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 725 | if (vreg->corner[i].floor_volt | 
|  | 726 | > vreg->corner[i].ceiling_volt) { | 
|  | 727 | cpr3_err(vreg, "CPR floor[%d]=%d > ceiling[%d]=%d uV\n", | 
|  | 728 | i, vreg->corner[i].floor_volt, | 
|  | 729 | i, vreg->corner[i].ceiling_volt); | 
|  | 730 | rc = -EINVAL; | 
|  | 731 | goto free_temp; | 
|  | 732 | } | 
|  | 733 | } | 
|  | 734 |  | 
|  | 735 | /* Load optional system-supply voltages */ | 
|  | 736 | if (of_find_property(vreg->of_node, "qcom,system-voltage", NULL)) { | 
|  | 737 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 738 | "qcom,system-voltage", 1, temp); | 
|  | 739 | if (rc) | 
|  | 740 | goto free_temp; | 
|  | 741 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 742 | vreg->corner[i].system_volt = temp[i]; | 
|  | 743 | } | 
|  | 744 |  | 
|  | 745 | rc = cpr3_parse_corner_array_property(vreg, "qcom,corner-frequencies", | 
|  | 746 | 1, temp); | 
|  | 747 | if (rc) | 
|  | 748 | goto free_temp; | 
|  | 749 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 750 | vreg->corner[i].proc_freq = temp[i]; | 
|  | 751 |  | 
|  | 752 | /* Validate frequencies */ | 
|  | 753 | for (i = 1; i < vreg->corner_count; i++) { | 
|  | 754 | if (vreg->corner[i].proc_freq | 
|  | 755 | < vreg->corner[i - 1].proc_freq) { | 
|  | 756 | cpr3_err(vreg, "invalid frequency: freq[%d]=%u < freq[%d]=%u\n", | 
|  | 757 | i, vreg->corner[i].proc_freq, i - 1, | 
|  | 758 | vreg->corner[i - 1].proc_freq); | 
|  | 759 | rc = -EINVAL; | 
|  | 760 | goto free_temp; | 
|  | 761 | } | 
|  | 762 | } | 
|  | 763 |  | 
|  | 764 | vreg->fuse_corner_map = devm_kcalloc(ctrl->dev, vreg->fuse_corner_count, | 
|  | 765 | sizeof(*vreg->fuse_corner_map), GFP_KERNEL); | 
|  | 766 | if (!vreg->fuse_corner_map) { | 
|  | 767 | rc = -ENOMEM; | 
|  | 768 | goto free_temp; | 
|  | 769 | } | 
|  | 770 |  | 
|  | 771 | rc = cpr3_parse_array_property(vreg, "qcom,cpr-corner-fmax-map", | 
|  | 772 | vreg->fuse_corner_count, temp); | 
|  | 773 | if (rc) | 
|  | 774 | goto free_temp; | 
|  | 775 | for (i = 0; i < vreg->fuse_corner_count; i++) { | 
|  | 776 | vreg->fuse_corner_map[i] = temp[i] - CPR3_CORNER_OFFSET; | 
|  | 777 | if (temp[i] < CPR3_CORNER_OFFSET | 
|  | 778 | || temp[i] > vreg->corner_count + CPR3_CORNER_OFFSET) { | 
|  | 779 | cpr3_err(vreg, "invalid corner value specified in qcom,cpr-corner-fmax-map: %u\n", | 
|  | 780 | temp[i]); | 
|  | 781 | rc = -EINVAL; | 
|  | 782 | goto free_temp; | 
|  | 783 | } else if (i > 0 && temp[i - 1] >= temp[i]) { | 
|  | 784 | cpr3_err(vreg, "invalid corner %u less than or equal to previous corner %u\n", | 
|  | 785 | temp[i], temp[i - 1]); | 
|  | 786 | rc = -EINVAL; | 
|  | 787 | goto free_temp; | 
|  | 788 | } | 
|  | 789 | } | 
|  | 790 | if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count) | 
|  | 791 | cpr3_debug(vreg, "Note: highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n", | 
|  | 792 | temp[vreg->fuse_corner_count - 1], | 
|  | 793 | vreg->corner_count); | 
|  | 794 |  | 
|  | 795 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 796 | for (j = 0; j < vreg->fuse_corner_count; j++) { | 
|  | 797 | if (i + CPR3_CORNER_OFFSET <= temp[j]) { | 
|  | 798 | vreg->corner[i].cpr_fuse_corner = j; | 
|  | 799 | break; | 
|  | 800 | } | 
|  | 801 | } | 
|  | 802 | if (j == vreg->fuse_corner_count) { | 
|  | 803 | /* | 
|  | 804 | * Handle the case where the highest fuse corner maps | 
|  | 805 | * to a corner below the highest corner. | 
|  | 806 | */ | 
|  | 807 | vreg->corner[i].cpr_fuse_corner | 
|  | 808 | = vreg->fuse_corner_count - 1; | 
|  | 809 | } | 
|  | 810 | } | 
|  | 811 |  | 
|  | 812 | if (of_find_property(vreg->of_node, | 
|  | 813 | "qcom,allow-aging-voltage-adjustment", NULL)) { | 
|  | 814 | rc = cpr3_parse_array_property(vreg, | 
|  | 815 | "qcom,allow-aging-voltage-adjustment", | 
|  | 816 | 1, &aging_allowed); | 
|  | 817 | if (rc) | 
|  | 818 | goto free_temp; | 
|  | 819 |  | 
|  | 820 | vreg->aging_allowed = aging_allowed; | 
|  | 821 | } | 
|  | 822 |  | 
|  | 823 | if (of_find_property(vreg->of_node, | 
|  | 824 | "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) { | 
|  | 825 | rc = cpr3_parse_array_property(vreg, | 
|  | 826 | "qcom,allow-aging-open-loop-voltage-adjustment", | 
|  | 827 | 1, &aging_allowed); | 
|  | 828 | if (rc) | 
|  | 829 | goto free_temp; | 
|  | 830 |  | 
|  | 831 | vreg->aging_allow_open_loop_adj = aging_allowed; | 
|  | 832 | } | 
|  | 833 |  | 
|  | 834 | if (vreg->aging_allowed) { | 
|  | 835 | if (ctrl->aging_ref_volt <= 0) { | 
|  | 836 | cpr3_err(ctrl, "qcom,cpr-aging-ref-voltage must be specified\n"); | 
|  | 837 | rc = -EINVAL; | 
|  | 838 | goto free_temp; | 
|  | 839 | } | 
|  | 840 |  | 
|  | 841 | rc = cpr3_parse_array_property(vreg, | 
|  | 842 | "qcom,cpr-aging-max-voltage-adjustment", | 
|  | 843 | 1, &vreg->aging_max_adjust_volt); | 
|  | 844 | if (rc) | 
|  | 845 | goto free_temp; | 
|  | 846 |  | 
|  | 847 | rc = cpr3_parse_array_property(vreg, | 
|  | 848 | "qcom,cpr-aging-ref-corner", 1, &vreg->aging_corner); | 
|  | 849 | if (rc) { | 
|  | 850 | goto free_temp; | 
|  | 851 | } else if (vreg->aging_corner < CPR3_CORNER_OFFSET | 
|  | 852 | || vreg->aging_corner > vreg->corner_count - 1 | 
|  | 853 | + CPR3_CORNER_OFFSET) { | 
|  | 854 | cpr3_err(vreg, "aging reference corner=%d not in range [%d, %d]\n", | 
|  | 855 | vreg->aging_corner, CPR3_CORNER_OFFSET, | 
|  | 856 | vreg->corner_count - 1 + CPR3_CORNER_OFFSET); | 
|  | 857 | rc = -EINVAL; | 
|  | 858 | goto free_temp; | 
|  | 859 | } | 
|  | 860 | vreg->aging_corner -= CPR3_CORNER_OFFSET; | 
|  | 861 |  | 
|  | 862 | if (of_find_property(vreg->of_node, "qcom,cpr-aging-derate", | 
|  | 863 | NULL)) { | 
|  | 864 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 865 | "qcom,cpr-aging-derate", 1, temp); | 
|  | 866 | if (rc) | 
|  | 867 | goto free_temp; | 
|  | 868 |  | 
|  | 869 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 870 | vreg->corner[i].aging_derate = temp[i]; | 
|  | 871 | } else { | 
|  | 872 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 873 | vreg->corner[i].aging_derate | 
|  | 874 | = CPR3_AGING_DERATE_UNITY; | 
|  | 875 | } | 
|  | 876 | } | 
|  | 877 |  | 
|  | 878 | free_temp: | 
|  | 879 | kfree(temp); | 
|  | 880 | return rc; | 
|  | 881 | } | 
|  | 882 |  | 
|  | 883 | /** | 
|  | 884 | * cpr3_parse_thread_u32() - parse the specified property from the CPR3 thread's | 
|  | 885 | *		device tree node and verify that it is within the allowed limits | 
|  | 886 | * @thread:		Pointer to the CPR3 thread | 
|  | 887 | * @propname:		The name of the device tree property to read | 
|  | 888 | * @out_value:		The output pointer to fill with the value read | 
|  | 889 | * @value_min:		The minimum allowed property value | 
|  | 890 | * @value_max:		The maximum allowed property value | 
|  | 891 | * | 
|  | 892 | * This function prints a verbose error message if the property is missing or | 
|  | 893 | * has a value which is not within the specified range. | 
|  | 894 | * | 
|  | 895 | * Return: 0 on success, errno on failure | 
|  | 896 | */ | 
|  | 897 | int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, | 
|  | 898 | u32 *out_value, u32 value_min, u32 value_max) | 
|  | 899 | { | 
|  | 900 | int rc; | 
|  | 901 |  | 
|  | 902 | rc = of_property_read_u32(thread->of_node, propname, out_value); | 
|  | 903 | if (rc) { | 
|  | 904 | cpr3_err(thread->ctrl, "thread %u error reading property %s, rc=%d\n", | 
|  | 905 | thread->thread_id, propname, rc); | 
|  | 906 | return rc; | 
|  | 907 | } | 
|  | 908 |  | 
|  | 909 | if (*out_value < value_min || *out_value > value_max) { | 
|  | 910 | cpr3_err(thread->ctrl, "thread %u %s=%u is invalid; allowed range: [%u, %u]\n", | 
|  | 911 | thread->thread_id, propname, *out_value, value_min, | 
|  | 912 | value_max); | 
|  | 913 | return -EINVAL; | 
|  | 914 | } | 
|  | 915 |  | 
|  | 916 | return 0; | 
|  | 917 | } | 
|  | 918 |  | 
|  | 919 | /** | 
|  | 920 | * cpr3_parse_ctrl_u32() - parse the specified property from the CPR3 | 
|  | 921 | *		controller's device tree node and verify that it is within the | 
|  | 922 | *		allowed limits | 
|  | 923 | * @ctrl:		Pointer to the CPR3 controller | 
|  | 924 | * @propname:		The name of the device tree property to read | 
|  | 925 | * @out_value:		The output pointer to fill with the value read | 
|  | 926 | * @value_min:		The minimum allowed property value | 
|  | 927 | * @value_max:		The maximum allowed property value | 
|  | 928 | * | 
|  | 929 | * This function prints a verbose error message if the property is missing or | 
|  | 930 | * has a value which is not within the specified range. | 
|  | 931 | * | 
|  | 932 | * Return: 0 on success, errno on failure | 
|  | 933 | */ | 
|  | 934 | int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, | 
|  | 935 | u32 *out_value, u32 value_min, u32 value_max) | 
|  | 936 | { | 
|  | 937 | int rc; | 
|  | 938 |  | 
|  | 939 | rc = of_property_read_u32(ctrl->dev->of_node, propname, out_value); | 
|  | 940 | if (rc) { | 
|  | 941 | cpr3_err(ctrl, "error reading property %s, rc=%d\n", | 
|  | 942 | propname, rc); | 
|  | 943 | return rc; | 
|  | 944 | } | 
|  | 945 |  | 
|  | 946 | if (*out_value < value_min || *out_value > value_max) { | 
|  | 947 | cpr3_err(ctrl, "%s=%u is invalid; allowed range: [%u, %u]\n", | 
|  | 948 | propname, *out_value, value_min, value_max); | 
|  | 949 | return -EINVAL; | 
|  | 950 | } | 
|  | 951 |  | 
|  | 952 | return 0; | 
|  | 953 | } | 
|  | 954 |  | 
|  | 955 | /** | 
|  | 956 | * cpr3_parse_common_thread_data() - parse common CPR3 thread properties from | 
|  | 957 | *		device tree | 
|  | 958 | * @thread:		Pointer to the CPR3 thread | 
|  | 959 | * | 
|  | 960 | * Return: 0 on success, errno on failure | 
|  | 961 | */ | 
|  | 962 | int cpr3_parse_common_thread_data(struct cpr3_thread *thread) | 
|  | 963 | { | 
|  | 964 | int rc; | 
|  | 965 |  | 
|  | 966 | rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-up", | 
|  | 967 | &thread->consecutive_up, CPR3_CONSECUTIVE_UP_DOWN_MIN, | 
|  | 968 | CPR3_CONSECUTIVE_UP_DOWN_MAX); | 
|  | 969 | if (rc) | 
|  | 970 | return rc; | 
|  | 971 |  | 
|  | 972 | rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-down", | 
|  | 973 | &thread->consecutive_down, CPR3_CONSECUTIVE_UP_DOWN_MIN, | 
|  | 974 | CPR3_CONSECUTIVE_UP_DOWN_MAX); | 
|  | 975 | if (rc) | 
|  | 976 | return rc; | 
|  | 977 |  | 
|  | 978 | rc = cpr3_parse_thread_u32(thread, "qcom,cpr-up-threshold", | 
|  | 979 | &thread->up_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, | 
|  | 980 | CPR3_UP_DOWN_THRESHOLD_MAX); | 
|  | 981 | if (rc) | 
|  | 982 | return rc; | 
|  | 983 |  | 
|  | 984 | rc = cpr3_parse_thread_u32(thread, "qcom,cpr-down-threshold", | 
|  | 985 | &thread->down_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, | 
|  | 986 | CPR3_UP_DOWN_THRESHOLD_MAX); | 
|  | 987 | if (rc) | 
|  | 988 | return rc; | 
|  | 989 |  | 
|  | 990 | return rc; | 
|  | 991 | } | 
|  | 992 |  | 
|  | 993 | /** | 
|  | 994 | * cpr3_parse_irq_affinity() - parse CPR IRQ affinity information | 
|  | 995 | * @ctrl:		Pointer to the CPR3 controller | 
|  | 996 | * | 
|  | 997 | * Return: 0 on success, errno on failure | 
|  | 998 | */ | 
|  | 999 | static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl) | 
|  | 1000 | { | 
|  | 1001 | struct device_node *cpu_node; | 
|  | 1002 | int i, cpu; | 
|  | 1003 | int len = 0; | 
|  | 1004 |  | 
|  | 1005 | if (!of_find_property(ctrl->dev->of_node, "qcom,cpr-interrupt-affinity", | 
|  | 1006 | &len)) { | 
|  | 1007 | /* No IRQ affinity required */ | 
|  | 1008 | return 0; | 
|  | 1009 | } | 
|  | 1010 |  | 
|  | 1011 | len /= sizeof(u32); | 
|  | 1012 |  | 
|  | 1013 | for (i = 0; i < len; i++) { | 
|  | 1014 | cpu_node = of_parse_phandle(ctrl->dev->of_node, | 
|  | 1015 | "qcom,cpr-interrupt-affinity", i); | 
|  | 1016 | if (!cpu_node) { | 
|  | 1017 | cpr3_err(ctrl, "could not find CPU node %d\n", i); | 
|  | 1018 | return -EINVAL; | 
|  | 1019 | } | 
|  | 1020 |  | 
|  | 1021 | for_each_possible_cpu(cpu) { | 
|  | 1022 | if (of_get_cpu_node(cpu, NULL) == cpu_node) { | 
|  | 1023 | cpumask_set_cpu(cpu, &ctrl->irq_affinity_mask); | 
|  | 1024 | break; | 
|  | 1025 | } | 
|  | 1026 | } | 
|  | 1027 | of_node_put(cpu_node); | 
|  | 1028 | } | 
|  | 1029 |  | 
|  | 1030 | return 0; | 
|  | 1031 | } | 
|  | 1032 |  | 
|  | 1033 | static int cpr3_panic_notifier_init(struct cpr3_controller *ctrl) | 
|  | 1034 | { | 
|  | 1035 | struct device_node *node = ctrl->dev->of_node; | 
|  | 1036 | struct cpr3_panic_regs_info *panic_regs_info; | 
|  | 1037 | struct cpr3_reg_info *regs; | 
|  | 1038 | int i, reg_count, len, rc = 0; | 
|  | 1039 |  | 
|  | 1040 | if (!of_find_property(node, "qcom,cpr-panic-reg-addr-list", &len)) { | 
|  | 1041 | /* panic register address list not specified */ | 
|  | 1042 | return rc; | 
|  | 1043 | } | 
|  | 1044 |  | 
|  | 1045 | reg_count = len / sizeof(u32); | 
|  | 1046 | if (!reg_count) { | 
|  | 1047 | cpr3_err(ctrl, "qcom,cpr-panic-reg-addr-list has invalid len = %d\n", | 
|  | 1048 | len); | 
|  | 1049 | return -EINVAL; | 
|  | 1050 | } | 
|  | 1051 |  | 
|  | 1052 | if (!of_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) { | 
|  | 1053 | cpr3_err(ctrl, "property qcom,cpr-panic-reg-name-list not specified\n"); | 
|  | 1054 | return -EINVAL; | 
|  | 1055 | } | 
|  | 1056 |  | 
|  | 1057 | len = of_property_count_strings(node, "qcom,cpr-panic-reg-name-list"); | 
|  | 1058 | if (reg_count != len) { | 
|  | 1059 | cpr3_err(ctrl, "qcom,cpr-panic-reg-name-list should have %d strings\n", | 
|  | 1060 | reg_count); | 
|  | 1061 | return -EINVAL; | 
|  | 1062 | } | 
|  | 1063 |  | 
|  | 1064 | panic_regs_info = devm_kzalloc(ctrl->dev, sizeof(*panic_regs_info), | 
|  | 1065 | GFP_KERNEL); | 
|  | 1066 | if (!panic_regs_info) | 
|  | 1067 | return -ENOMEM; | 
|  | 1068 |  | 
|  | 1069 | regs = devm_kcalloc(ctrl->dev, reg_count, sizeof(*regs), GFP_KERNEL); | 
|  | 1070 | if (!regs) | 
|  | 1071 | return -ENOMEM; | 
|  | 1072 |  | 
|  | 1073 | for (i = 0; i < reg_count; i++) { | 
|  | 1074 | rc = of_property_read_string_index(node, | 
|  | 1075 | "qcom,cpr-panic-reg-name-list", i, | 
|  | 1076 | &(regs[i].name)); | 
|  | 1077 | if (rc) { | 
|  | 1078 | cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-name-list, rc=%d\n", | 
|  | 1079 | rc); | 
|  | 1080 | return rc; | 
|  | 1081 | } | 
|  | 1082 |  | 
|  | 1083 | rc = of_property_read_u32_index(node, | 
|  | 1084 | "qcom,cpr-panic-reg-addr-list", i, | 
|  | 1085 | &(regs[i].addr)); | 
|  | 1086 | if (rc) { | 
|  | 1087 | cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-addr-list, rc=%d\n", | 
|  | 1088 | rc); | 
|  | 1089 | return rc; | 
|  | 1090 | } | 
|  | 1091 | regs[i].virt_addr = devm_ioremap(ctrl->dev, regs[i].addr, 0x4); | 
|  | 1092 | if (!regs[i].virt_addr) { | 
|  | 1093 | pr_err("Unable to map panic register addr 0x%08x\n", | 
|  | 1094 | regs[i].addr); | 
|  | 1095 | return -EINVAL; | 
|  | 1096 | } | 
|  | 1097 | regs[i].value = 0xFFFFFFFF; | 
|  | 1098 | } | 
|  | 1099 |  | 
|  | 1100 | panic_regs_info->reg_count = reg_count; | 
|  | 1101 | panic_regs_info->regs = regs; | 
|  | 1102 | ctrl->panic_regs_info = panic_regs_info; | 
|  | 1103 |  | 
|  | 1104 | return rc; | 
|  | 1105 | } | 
|  | 1106 |  | 
|  | 1107 | /** | 
|  | 1108 | * cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from | 
|  | 1109 | *		device tree | 
|  | 1110 | * @ctrl:		Pointer to the CPR3 controller | 
|  | 1111 | * | 
|  | 1112 | * Return: 0 on success, errno on failure | 
|  | 1113 | */ | 
|  | 1114 | int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) | 
|  | 1115 | { | 
|  | 1116 | int rc; | 
|  | 1117 |  | 
|  | 1118 | rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-sensor-time", | 
|  | 1119 | &ctrl->sensor_time, 0, UINT_MAX); | 
|  | 1120 | if (rc) | 
|  | 1121 | return rc; | 
|  | 1122 |  | 
|  | 1123 | rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-loop-time", | 
|  | 1124 | &ctrl->loop_time, 0, UINT_MAX); | 
|  | 1125 | if (rc) | 
|  | 1126 | return rc; | 
|  | 1127 |  | 
|  | 1128 | rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-idle-cycles", | 
|  | 1129 | &ctrl->idle_clocks, CPR3_IDLE_CLOCKS_MIN, | 
|  | 1130 | CPR3_IDLE_CLOCKS_MAX); | 
|  | 1131 | if (rc) | 
|  | 1132 | return rc; | 
|  | 1133 |  | 
|  | 1134 | rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-min", | 
|  | 1135 | &ctrl->step_quot_init_min, CPR3_STEP_QUOT_MIN, | 
|  | 1136 | CPR3_STEP_QUOT_MAX); | 
|  | 1137 | if (rc) | 
|  | 1138 | return rc; | 
|  | 1139 |  | 
|  | 1140 | rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-max", | 
|  | 1141 | &ctrl->step_quot_init_max, CPR3_STEP_QUOT_MIN, | 
|  | 1142 | CPR3_STEP_QUOT_MAX); | 
|  | 1143 | if (rc) | 
|  | 1144 | return rc; | 
|  | 1145 |  | 
|  | 1146 | rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", | 
|  | 1147 | &ctrl->step_volt); | 
|  | 1148 | if (rc) { | 
|  | 1149 | cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", | 
|  | 1150 | rc); | 
|  | 1151 | return rc; | 
|  | 1152 | } | 
|  | 1153 | if (ctrl->step_volt <= 0) { | 
|  | 1154 | cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", | 
|  | 1155 | ctrl->step_volt); | 
|  | 1156 | return -EINVAL; | 
|  | 1157 | } | 
|  | 1158 |  | 
|  | 1159 | rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-count-mode", | 
|  | 1160 | &ctrl->count_mode, CPR3_COUNT_MODE_ALL_AT_ONCE_MIN, | 
|  | 1161 | CPR3_COUNT_MODE_STAGGERED); | 
|  | 1162 | if (rc) | 
|  | 1163 | return rc; | 
|  | 1164 |  | 
|  | 1165 | /* Count repeat is optional */ | 
|  | 1166 | ctrl->count_repeat = 0; | 
|  | 1167 | of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-count-repeat", | 
|  | 1168 | &ctrl->count_repeat); | 
|  | 1169 |  | 
|  | 1170 | ctrl->cpr_allowed_sw = of_property_read_bool(ctrl->dev->of_node, | 
|  | 1171 | "qcom,cpr-enable"); | 
|  | 1172 |  | 
|  | 1173 | rc = cpr3_parse_irq_affinity(ctrl); | 
|  | 1174 | if (rc) | 
|  | 1175 | return rc; | 
|  | 1176 |  | 
| David Collins | 044e9e7 | 2017-03-06 16:47:09 -0800 | [diff] [blame] | 1177 | ctrl->ignore_invalid_fuses = of_property_read_bool(ctrl->dev->of_node, | 
|  | 1178 | "qcom,cpr-ignore-invalid-fuses"); | 
|  | 1179 |  | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 1180 | /* Aging reference voltage is optional */ | 
|  | 1181 | ctrl->aging_ref_volt = 0; | 
|  | 1182 | of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage", | 
|  | 1183 | &ctrl->aging_ref_volt); | 
|  | 1184 |  | 
|  | 1185 | /* Aging possible bitmask is optional */ | 
|  | 1186 | ctrl->aging_possible_mask = 0; | 
|  | 1187 | of_property_read_u32(ctrl->dev->of_node, | 
|  | 1188 | "qcom,cpr-aging-allowed-reg-mask", | 
|  | 1189 | &ctrl->aging_possible_mask); | 
|  | 1190 |  | 
|  | 1191 | if (ctrl->aging_possible_mask) { | 
|  | 1192 | /* | 
|  | 1193 | * Aging possible register value required if bitmask is | 
|  | 1194 | * specified | 
|  | 1195 | */ | 
|  | 1196 | rc = cpr3_parse_ctrl_u32(ctrl, | 
|  | 1197 | "qcom,cpr-aging-allowed-reg-value", | 
|  | 1198 | &ctrl->aging_possible_val, 0, UINT_MAX); | 
|  | 1199 | if (rc) | 
|  | 1200 | return rc; | 
|  | 1201 | } | 
|  | 1202 |  | 
|  | 1203 | if (of_find_property(ctrl->dev->of_node, "clock-names", NULL)) { | 
|  | 1204 | ctrl->core_clk = devm_clk_get(ctrl->dev, "core_clk"); | 
|  | 1205 | if (IS_ERR(ctrl->core_clk)) { | 
|  | 1206 | rc = PTR_ERR(ctrl->core_clk); | 
|  | 1207 | if (rc != -EPROBE_DEFER) | 
|  | 1208 | cpr3_err(ctrl, "unable request core clock, rc=%d\n", | 
|  | 1209 | rc); | 
|  | 1210 | return rc; | 
|  | 1211 | } | 
|  | 1212 | } | 
|  | 1213 |  | 
|  | 1214 | rc = cpr3_panic_notifier_init(ctrl); | 
|  | 1215 | if (rc) | 
|  | 1216 | return rc; | 
|  | 1217 |  | 
|  | 1218 | if (of_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { | 
|  | 1219 | ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); | 
|  | 1220 | if (IS_ERR(ctrl->vdd_regulator)) { | 
|  | 1221 | rc = PTR_ERR(ctrl->vdd_regulator); | 
|  | 1222 | if (rc != -EPROBE_DEFER) | 
|  | 1223 | cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", | 
|  | 1224 | rc); | 
|  | 1225 | return rc; | 
|  | 1226 | } | 
|  | 1227 | } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) { | 
|  | 1228 | /* vdd-supply is optional for CPRh controllers. */ | 
|  | 1229 | ctrl->vdd_regulator = NULL; | 
|  | 1230 | } else { | 
|  | 1231 | cpr3_err(ctrl, "vdd supply is not defined\n"); | 
|  | 1232 | return -ENODEV; | 
|  | 1233 | } | 
|  | 1234 |  | 
|  | 1235 | /* | 
| Tirupathi Reddy | 718bc80 | 2017-02-09 16:29:24 +0530 | [diff] [blame] | 1236 | * Reset step_quot to default on each loop_en = 0 transition is | 
|  | 1237 | * optional. | 
|  | 1238 | */ | 
|  | 1239 | ctrl->reset_step_quot_loop_en | 
|  | 1240 | = of_property_read_bool(ctrl->dev->of_node, | 
|  | 1241 | "qcom,cpr-reset-step-quot-loop-en"); | 
|  | 1242 |  | 
|  | 1243 | /* | 
| David Collins | 7370f1a | 2017-01-18 16:21:53 -0800 | [diff] [blame] | 1244 | * Regulator device handles are not necessary for CPRh controllers | 
|  | 1245 | * since communication with the regulators is completely managed | 
|  | 1246 | * in hardware. | 
|  | 1247 | */ | 
|  | 1248 | if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) | 
|  | 1249 | return rc; | 
|  | 1250 |  | 
|  | 1251 | ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, | 
|  | 1252 | "system"); | 
|  | 1253 | if (IS_ERR(ctrl->system_regulator)) { | 
|  | 1254 | rc = PTR_ERR(ctrl->system_regulator); | 
|  | 1255 | if (rc != -EPROBE_DEFER) { | 
|  | 1256 | rc = 0; | 
|  | 1257 | ctrl->system_regulator = NULL; | 
|  | 1258 | } else { | 
|  | 1259 | return rc; | 
|  | 1260 | } | 
|  | 1261 | } | 
|  | 1262 |  | 
|  | 1263 | ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, | 
|  | 1264 | "mem-acc"); | 
|  | 1265 | if (IS_ERR(ctrl->mem_acc_regulator)) { | 
|  | 1266 | rc = PTR_ERR(ctrl->mem_acc_regulator); | 
|  | 1267 | if (rc != -EPROBE_DEFER) { | 
|  | 1268 | rc = 0; | 
|  | 1269 | ctrl->mem_acc_regulator = NULL; | 
|  | 1270 | } else { | 
|  | 1271 | return rc; | 
|  | 1272 | } | 
|  | 1273 | } | 
|  | 1274 |  | 
|  | 1275 | return rc; | 
|  | 1276 | } | 
|  | 1277 |  | 
|  | 1278 | /** | 
|  | 1279 | * cpr3_limit_open_loop_voltages() - modify the open-loop voltage of each corner | 
|  | 1280 | *				so that it fits within the floor to ceiling | 
|  | 1281 | *				voltage range of the corner | 
|  | 1282 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1283 | * | 
|  | 1284 | * This function clips the open-loop voltage for each corner so that it is | 
|  | 1285 | * limited to the floor to ceiling range.  It also rounds each open-loop voltage | 
|  | 1286 | * so that it corresponds to a set point available to the underlying regulator. | 
|  | 1287 | * | 
|  | 1288 | * Return: 0 on success, errno on failure | 
|  | 1289 | */ | 
|  | 1290 | int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) | 
|  | 1291 | { | 
|  | 1292 | int i, volt; | 
|  | 1293 |  | 
|  | 1294 | cpr3_debug(vreg, "open-loop voltages after trimming and rounding:\n"); | 
|  | 1295 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 1296 | volt = CPR3_ROUND(vreg->corner[i].open_loop_volt, | 
|  | 1297 | vreg->thread->ctrl->step_volt); | 
|  | 1298 | if (volt < vreg->corner[i].floor_volt) | 
|  | 1299 | volt = vreg->corner[i].floor_volt; | 
|  | 1300 | else if (volt > vreg->corner[i].ceiling_volt) | 
|  | 1301 | volt = vreg->corner[i].ceiling_volt; | 
|  | 1302 | vreg->corner[i].open_loop_volt = volt; | 
|  | 1303 | cpr3_debug(vreg, "corner[%2d]: open-loop=%d uV\n", i, volt); | 
|  | 1304 | } | 
|  | 1305 |  | 
|  | 1306 | return 0; | 
|  | 1307 | } | 
|  | 1308 |  | 
|  | 1309 | /** | 
|  | 1310 | * cpr3_open_loop_voltage_as_ceiling() - configures the ceiling voltage for each | 
|  | 1311 | *		corner to equal the open-loop voltage if the relevant device | 
|  | 1312 | *		tree property is found for the CPR3 regulator | 
|  | 1313 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1314 | * | 
|  | 1315 | * This function assumes that the the open-loop voltage for each corner has | 
|  | 1316 | * already been rounded to the nearest allowed set point and that it falls | 
|  | 1317 | * within the floor to ceiling range. | 
|  | 1318 | * | 
|  | 1319 | * Return: none | 
|  | 1320 | */ | 
|  | 1321 | void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg) | 
|  | 1322 | { | 
|  | 1323 | int i; | 
|  | 1324 |  | 
|  | 1325 | if (!of_property_read_bool(vreg->of_node, | 
|  | 1326 | "qcom,cpr-scaled-open-loop-voltage-as-ceiling")) | 
|  | 1327 | return; | 
|  | 1328 |  | 
|  | 1329 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 1330 | vreg->corner[i].ceiling_volt | 
|  | 1331 | = vreg->corner[i].open_loop_volt; | 
|  | 1332 | } | 
|  | 1333 |  | 
|  | 1334 | /** | 
|  | 1335 | * cpr3_limit_floor_voltages() - raise the floor voltage of each corner so that | 
|  | 1336 | *		the optional maximum floor to ceiling voltage range specified in | 
|  | 1337 | *		device tree is satisfied | 
|  | 1338 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1339 | * | 
|  | 1340 | * This function also ensures that the open-loop voltage for each corner falls | 
|  | 1341 | * within the final floor to ceiling voltage range and that floor voltages | 
|  | 1342 | * increase monotonically. | 
|  | 1343 | * | 
|  | 1344 | * Return: 0 on success, errno on failure | 
|  | 1345 | */ | 
|  | 1346 | int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) | 
|  | 1347 | { | 
|  | 1348 | char *prop = "qcom,cpr-floor-to-ceiling-max-range"; | 
|  | 1349 | int i, floor_new; | 
|  | 1350 | u32 *floor_range; | 
|  | 1351 | int rc = 0; | 
|  | 1352 |  | 
|  | 1353 | if (!of_find_property(vreg->of_node, prop, NULL)) | 
|  | 1354 | goto enforce_monotonicity; | 
|  | 1355 |  | 
|  | 1356 | floor_range = kcalloc(vreg->corner_count, sizeof(*floor_range), | 
|  | 1357 | GFP_KERNEL); | 
|  | 1358 | if (!floor_range) | 
|  | 1359 | return -ENOMEM; | 
|  | 1360 |  | 
|  | 1361 | rc = cpr3_parse_corner_array_property(vreg, prop, 1, floor_range); | 
|  | 1362 | if (rc) | 
|  | 1363 | goto free_floor_adjust; | 
|  | 1364 |  | 
|  | 1365 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 1366 | if ((s32)floor_range[i] >= 0) { | 
|  | 1367 | floor_new = CPR3_ROUND(vreg->corner[i].ceiling_volt | 
|  | 1368 | - floor_range[i], | 
|  | 1369 | vreg->thread->ctrl->step_volt); | 
|  | 1370 |  | 
|  | 1371 | vreg->corner[i].floor_volt = max(floor_new, | 
|  | 1372 | vreg->corner[i].floor_volt); | 
|  | 1373 | if (vreg->corner[i].open_loop_volt | 
|  | 1374 | < vreg->corner[i].floor_volt) | 
|  | 1375 | vreg->corner[i].open_loop_volt | 
|  | 1376 | = vreg->corner[i].floor_volt; | 
|  | 1377 | } | 
|  | 1378 | } | 
|  | 1379 |  | 
|  | 1380 | free_floor_adjust: | 
|  | 1381 | kfree(floor_range); | 
|  | 1382 |  | 
|  | 1383 | enforce_monotonicity: | 
|  | 1384 | /* Ensure that floor voltages increase monotonically. */ | 
|  | 1385 | for (i = 1; i < vreg->corner_count; i++) { | 
|  | 1386 | if (vreg->corner[i].floor_volt | 
|  | 1387 | < vreg->corner[i - 1].floor_volt) { | 
|  | 1388 | cpr3_debug(vreg, "corner %d floor voltage=%d uV < corner %d voltage=%d uV; overriding: corner %d voltage=%d\n", | 
|  | 1389 | i, vreg->corner[i].floor_volt, | 
|  | 1390 | i - 1, vreg->corner[i - 1].floor_volt, | 
|  | 1391 | i, vreg->corner[i - 1].floor_volt); | 
|  | 1392 | vreg->corner[i].floor_volt | 
|  | 1393 | = vreg->corner[i - 1].floor_volt; | 
|  | 1394 |  | 
|  | 1395 | if (vreg->corner[i].open_loop_volt | 
|  | 1396 | < vreg->corner[i].floor_volt) | 
|  | 1397 | vreg->corner[i].open_loop_volt | 
|  | 1398 | = vreg->corner[i].floor_volt; | 
|  | 1399 | if (vreg->corner[i].ceiling_volt | 
|  | 1400 | < vreg->corner[i].floor_volt) | 
|  | 1401 | vreg->corner[i].ceiling_volt | 
|  | 1402 | = vreg->corner[i].floor_volt; | 
|  | 1403 | } | 
|  | 1404 | } | 
|  | 1405 |  | 
|  | 1406 | return rc; | 
|  | 1407 | } | 
|  | 1408 |  | 
|  | 1409 | /** | 
|  | 1410 | * cpr3_print_quots() - print CPR target quotients into the kernel log for | 
|  | 1411 | *		debugging purposes | 
|  | 1412 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1413 | * | 
|  | 1414 | * Return: none | 
|  | 1415 | */ | 
|  | 1416 | void cpr3_print_quots(struct cpr3_regulator *vreg) | 
|  | 1417 | { | 
|  | 1418 | int i, j, pos; | 
|  | 1419 | size_t buflen; | 
|  | 1420 | char *buf; | 
|  | 1421 |  | 
|  | 1422 | buflen = sizeof(*buf) * CPR3_RO_COUNT * (MAX_CHARS_PER_INT + 2); | 
|  | 1423 | buf = kzalloc(buflen, GFP_KERNEL); | 
|  | 1424 | if (!buf) | 
|  | 1425 | return; | 
|  | 1426 |  | 
|  | 1427 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 1428 | for (j = 0, pos = 0; j < CPR3_RO_COUNT; j++) | 
|  | 1429 | pos += scnprintf(buf + pos, buflen - pos, " %u", | 
|  | 1430 | vreg->corner[i].target_quot[j]); | 
|  | 1431 | cpr3_debug(vreg, "target quots[%2d]:%s\n", i, buf); | 
|  | 1432 | } | 
|  | 1433 |  | 
|  | 1434 | kfree(buf); | 
|  | 1435 | } | 
|  | 1436 |  | 
|  | 1437 | /** | 
|  | 1438 | * cpr3_adjust_fused_open_loop_voltages() - adjust the fused open-loop voltages | 
|  | 1439 | *		for each fuse corner according to device tree values | 
|  | 1440 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1441 | * @fuse_volt:		Pointer to an array of the fused open-loop voltage | 
|  | 1442 | *			values | 
|  | 1443 | * | 
|  | 1444 | * Voltage values in fuse_volt are modified in place. | 
|  | 1445 | * | 
|  | 1446 | * Return: 0 on success, errno on failure | 
|  | 1447 | */ | 
|  | 1448 | int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, | 
|  | 1449 | int *fuse_volt) | 
|  | 1450 | { | 
|  | 1451 | int i, rc, prev_volt; | 
|  | 1452 | int *volt_adjust; | 
|  | 1453 |  | 
|  | 1454 | if (!of_find_property(vreg->of_node, | 
|  | 1455 | "qcom,cpr-open-loop-voltage-fuse-adjustment", NULL)) { | 
|  | 1456 | /* No adjustment required. */ | 
|  | 1457 | return 0; | 
|  | 1458 | } | 
|  | 1459 |  | 
|  | 1460 | volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), | 
|  | 1461 | GFP_KERNEL); | 
|  | 1462 | if (!volt_adjust) | 
|  | 1463 | return -ENOMEM; | 
|  | 1464 |  | 
|  | 1465 | rc = cpr3_parse_array_property(vreg, | 
|  | 1466 | "qcom,cpr-open-loop-voltage-fuse-adjustment", | 
|  | 1467 | vreg->fuse_corner_count, volt_adjust); | 
|  | 1468 | if (rc) { | 
|  | 1469 | cpr3_err(vreg, "could not load open-loop fused voltage adjustments, rc=%d\n", | 
|  | 1470 | rc); | 
|  | 1471 | goto done; | 
|  | 1472 | } | 
|  | 1473 |  | 
|  | 1474 | for (i = 0; i < vreg->fuse_corner_count; i++) { | 
|  | 1475 | if (volt_adjust[i]) { | 
|  | 1476 | prev_volt = fuse_volt[i]; | 
|  | 1477 | fuse_volt[i] += volt_adjust[i]; | 
|  | 1478 | cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", | 
|  | 1479 | i, prev_volt, fuse_volt[i]); | 
|  | 1480 | } | 
|  | 1481 | } | 
|  | 1482 |  | 
|  | 1483 | done: | 
|  | 1484 | kfree(volt_adjust); | 
|  | 1485 | return rc; | 
|  | 1486 | } | 
|  | 1487 |  | 
|  | 1488 | /** | 
|  | 1489 | * cpr3_adjust_open_loop_voltages() - adjust the open-loop voltages for each | 
|  | 1490 | *		corner according to device tree values | 
|  | 1491 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1492 | * | 
|  | 1493 | * Return: 0 on success, errno on failure | 
|  | 1494 | */ | 
|  | 1495 | int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) | 
|  | 1496 | { | 
|  | 1497 | int i, rc, prev_volt, min_volt; | 
|  | 1498 | int *volt_adjust, *volt_diff; | 
|  | 1499 |  | 
|  | 1500 | if (!of_find_property(vreg->of_node, | 
|  | 1501 | "qcom,cpr-open-loop-voltage-adjustment", NULL)) { | 
|  | 1502 | /* No adjustment required. */ | 
|  | 1503 | return 0; | 
|  | 1504 | } | 
|  | 1505 |  | 
|  | 1506 | volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), | 
|  | 1507 | GFP_KERNEL); | 
|  | 1508 | volt_diff = kcalloc(vreg->corner_count, sizeof(*volt_diff), GFP_KERNEL); | 
|  | 1509 | if (!volt_adjust || !volt_diff) { | 
|  | 1510 | rc = -ENOMEM; | 
|  | 1511 | goto done; | 
|  | 1512 | } | 
|  | 1513 |  | 
|  | 1514 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 1515 | "qcom,cpr-open-loop-voltage-adjustment", 1, volt_adjust); | 
|  | 1516 | if (rc) { | 
|  | 1517 | cpr3_err(vreg, "could not load open-loop voltage adjustments, rc=%d\n", | 
|  | 1518 | rc); | 
|  | 1519 | goto done; | 
|  | 1520 | } | 
|  | 1521 |  | 
|  | 1522 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 1523 | if (volt_adjust[i]) { | 
|  | 1524 | prev_volt = vreg->corner[i].open_loop_volt; | 
|  | 1525 | vreg->corner[i].open_loop_volt += volt_adjust[i]; | 
|  | 1526 | cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", | 
|  | 1527 | i, prev_volt, vreg->corner[i].open_loop_volt); | 
|  | 1528 | } | 
|  | 1529 | } | 
|  | 1530 |  | 
|  | 1531 | if (of_find_property(vreg->of_node, | 
|  | 1532 | "qcom,cpr-open-loop-voltage-min-diff", NULL)) { | 
|  | 1533 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 1534 | "qcom,cpr-open-loop-voltage-min-diff", 1, volt_diff); | 
|  | 1535 | if (rc) { | 
|  | 1536 | cpr3_err(vreg, "could not load minimum open-loop voltage differences, rc=%d\n", | 
|  | 1537 | rc); | 
|  | 1538 | goto done; | 
|  | 1539 | } | 
|  | 1540 | } | 
|  | 1541 |  | 
|  | 1542 | /* | 
|  | 1543 | * Ensure that open-loop voltages increase monotonically with respect | 
|  | 1544 | * to configurable minimum allowed differences. | 
|  | 1545 | */ | 
|  | 1546 | for (i = 1; i < vreg->corner_count; i++) { | 
|  | 1547 | min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i]; | 
|  | 1548 | if (vreg->corner[i].open_loop_volt < min_volt) { | 
|  | 1549 | cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", | 
|  | 1550 | i, vreg->corner[i].open_loop_volt, | 
|  | 1551 | i - 1, vreg->corner[i - 1].open_loop_volt, | 
|  | 1552 | volt_diff[i], i, min_volt); | 
|  | 1553 | vreg->corner[i].open_loop_volt = min_volt; | 
|  | 1554 | } | 
|  | 1555 | } | 
|  | 1556 |  | 
|  | 1557 | done: | 
|  | 1558 | kfree(volt_diff); | 
|  | 1559 | kfree(volt_adjust); | 
|  | 1560 | return rc; | 
|  | 1561 | } | 
|  | 1562 |  | 
|  | 1563 | /** | 
|  | 1564 | * cpr3_quot_adjustment() - returns the quotient adjustment value resulting from | 
|  | 1565 | *		the specified voltage adjustment and RO scaling factor | 
|  | 1566 | * @ro_scale:		The CPR ring oscillator (RO) scaling factor with units | 
|  | 1567 | *			of QUOT/V | 
|  | 1568 | * @volt_adjust:	The amount to adjust the voltage by in units of | 
|  | 1569 | *			microvolts.  This value may be positive or negative. | 
|  | 1570 | */ | 
|  | 1571 | int cpr3_quot_adjustment(int ro_scale, int volt_adjust) | 
|  | 1572 | { | 
|  | 1573 | unsigned long long temp; | 
|  | 1574 | int quot_adjust; | 
|  | 1575 | int sign = 1; | 
|  | 1576 |  | 
|  | 1577 | if (ro_scale < 0) { | 
|  | 1578 | sign = -sign; | 
|  | 1579 | ro_scale = -ro_scale; | 
|  | 1580 | } | 
|  | 1581 |  | 
|  | 1582 | if (volt_adjust < 0) { | 
|  | 1583 | sign = -sign; | 
|  | 1584 | volt_adjust = -volt_adjust; | 
|  | 1585 | } | 
|  | 1586 |  | 
|  | 1587 | temp = (unsigned long long)ro_scale * (unsigned long long)volt_adjust; | 
|  | 1588 | do_div(temp, 1000000); | 
|  | 1589 |  | 
|  | 1590 | quot_adjust = temp; | 
|  | 1591 | quot_adjust *= sign; | 
|  | 1592 |  | 
|  | 1593 | return quot_adjust; | 
|  | 1594 | } | 
|  | 1595 |  | 
|  | 1596 | /** | 
|  | 1597 | * cpr3_voltage_adjustment() - returns the voltage adjustment value resulting | 
|  | 1598 | *		from the specified quotient adjustment and RO scaling factor | 
|  | 1599 | * @ro_scale:		The CPR ring oscillator (RO) scaling factor with units | 
|  | 1600 | *			of QUOT/V | 
|  | 1601 | * @quot_adjust:	The amount to adjust the quotient by in units of | 
|  | 1602 | *			QUOT.  This value may be positive or negative. | 
|  | 1603 | */ | 
|  | 1604 | int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) | 
|  | 1605 | { | 
|  | 1606 | unsigned long long temp; | 
|  | 1607 | int volt_adjust; | 
|  | 1608 | int sign = 1; | 
|  | 1609 |  | 
|  | 1610 | if (ro_scale < 0) { | 
|  | 1611 | sign = -sign; | 
|  | 1612 | ro_scale = -ro_scale; | 
|  | 1613 | } | 
|  | 1614 |  | 
|  | 1615 | if (quot_adjust < 0) { | 
|  | 1616 | sign = -sign; | 
|  | 1617 | quot_adjust = -quot_adjust; | 
|  | 1618 | } | 
|  | 1619 |  | 
|  | 1620 | if (ro_scale == 0) | 
|  | 1621 | return 0; | 
|  | 1622 |  | 
|  | 1623 | temp = (unsigned long long)quot_adjust * 1000000; | 
|  | 1624 | do_div(temp, ro_scale); | 
|  | 1625 |  | 
|  | 1626 | volt_adjust = temp; | 
|  | 1627 | volt_adjust *= sign; | 
|  | 1628 |  | 
|  | 1629 | return volt_adjust; | 
|  | 1630 | } | 
|  | 1631 |  | 
|  | 1632 | /** | 
|  | 1633 | * cpr3_parse_closed_loop_voltage_adjustments() - load per-fuse-corner and | 
|  | 1634 | *		per-corner closed-loop adjustment values from device tree | 
|  | 1635 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 1636 | * @ro_sel:		Array of ring oscillator values selected for each | 
|  | 1637 | *			fuse corner | 
|  | 1638 | * @volt_adjust:	Pointer to array which will be filled with the | 
|  | 1639 | *			per-corner closed-loop adjustment voltages | 
|  | 1640 | * @volt_adjust_fuse:	Pointer to array which will be filled with the | 
|  | 1641 | *			per-fuse-corner closed-loop adjustment voltages | 
|  | 1642 | * @ro_scale:		Pointer to array which will be filled with the | 
|  | 1643 | *			per-fuse-corner RO scaling factor values with units of | 
|  | 1644 | *			QUOT/V | 
|  | 1645 | * | 
|  | 1646 | * Return: 0 on success, errno on failure | 
|  | 1647 | */ | 
|  | 1648 | int cpr3_parse_closed_loop_voltage_adjustments( | 
|  | 1649 | struct cpr3_regulator *vreg, u64 *ro_sel, | 
|  | 1650 | int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) | 
|  | 1651 | { | 
|  | 1652 | int i, rc; | 
|  | 1653 | u32 *ro_all_scale; | 
|  | 1654 |  | 
|  | 1655 | if (!of_find_property(vreg->of_node, | 
|  | 1656 | "qcom,cpr-closed-loop-voltage-adjustment", NULL) | 
|  | 1657 | && !of_find_property(vreg->of_node, | 
|  | 1658 | "qcom,cpr-closed-loop-voltage-fuse-adjustment", NULL) | 
|  | 1659 | && !vreg->aging_allowed) { | 
|  | 1660 | /* No adjustment required. */ | 
|  | 1661 | return 0; | 
|  | 1662 | } else if (!of_find_property(vreg->of_node, | 
|  | 1663 | "qcom,cpr-ro-scaling-factor", NULL)) { | 
|  | 1664 | cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n"); | 
|  | 1665 | return -EINVAL; | 
|  | 1666 | } | 
|  | 1667 |  | 
|  | 1668 | ro_all_scale = kcalloc(vreg->fuse_corner_count * CPR3_RO_COUNT, | 
|  | 1669 | sizeof(*ro_all_scale), GFP_KERNEL); | 
|  | 1670 | if (!ro_all_scale) | 
|  | 1671 | return -ENOMEM; | 
|  | 1672 |  | 
|  | 1673 | rc = cpr3_parse_array_property(vreg, "qcom,cpr-ro-scaling-factor", | 
|  | 1674 | vreg->fuse_corner_count * CPR3_RO_COUNT, ro_all_scale); | 
|  | 1675 | if (rc) { | 
|  | 1676 | cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", | 
|  | 1677 | rc); | 
|  | 1678 | goto done; | 
|  | 1679 | } | 
|  | 1680 |  | 
|  | 1681 | for (i = 0; i < vreg->fuse_corner_count; i++) | 
|  | 1682 | ro_scale[i] = ro_all_scale[i * CPR3_RO_COUNT + ro_sel[i]]; | 
|  | 1683 |  | 
|  | 1684 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 1685 | memcpy(vreg->corner[i].ro_scale, | 
|  | 1686 | &ro_all_scale[vreg->corner[i].cpr_fuse_corner * CPR3_RO_COUNT], | 
|  | 1687 | sizeof(*ro_all_scale) * CPR3_RO_COUNT); | 
|  | 1688 |  | 
|  | 1689 | if (of_find_property(vreg->of_node, | 
|  | 1690 | "qcom,cpr-closed-loop-voltage-fuse-adjustment", NULL)) { | 
|  | 1691 | rc = cpr3_parse_array_property(vreg, | 
|  | 1692 | "qcom,cpr-closed-loop-voltage-fuse-adjustment", | 
|  | 1693 | vreg->fuse_corner_count, volt_adjust_fuse); | 
|  | 1694 | if (rc) { | 
|  | 1695 | cpr3_err(vreg, "could not load closed-loop fused voltage adjustments, rc=%d\n", | 
|  | 1696 | rc); | 
|  | 1697 | goto done; | 
|  | 1698 | } | 
|  | 1699 | } | 
|  | 1700 |  | 
|  | 1701 | if (of_find_property(vreg->of_node, | 
|  | 1702 | "qcom,cpr-closed-loop-voltage-adjustment", NULL)) { | 
|  | 1703 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 1704 | "qcom,cpr-closed-loop-voltage-adjustment", | 
|  | 1705 | 1, volt_adjust); | 
|  | 1706 | if (rc) { | 
|  | 1707 | cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", | 
|  | 1708 | rc); | 
|  | 1709 | goto done; | 
|  | 1710 | } | 
|  | 1711 | } | 
|  | 1712 |  | 
|  | 1713 | done: | 
|  | 1714 | kfree(ro_all_scale); | 
|  | 1715 | return rc; | 
|  | 1716 | } | 
|  | 1717 |  | 
|  | 1718 | /** | 
|  | 1719 | * cpr3_apm_init() - initialize APM data for a CPR3 controller | 
|  | 1720 | * @ctrl:		Pointer to the CPR3 controller | 
|  | 1721 | * | 
|  | 1722 | * This function loads memory array power mux (APM) data from device tree | 
|  | 1723 | * if it is present and requests a handle to the appropriate APM controller | 
|  | 1724 | * device. | 
|  | 1725 | * | 
|  | 1726 | * Return: 0 on success, errno on failure | 
|  | 1727 | */ | 
|  | 1728 | int cpr3_apm_init(struct cpr3_controller *ctrl) | 
|  | 1729 | { | 
|  | 1730 | struct device_node *node = ctrl->dev->of_node; | 
|  | 1731 | int rc; | 
|  | 1732 |  | 
|  | 1733 | if (!of_find_property(node, "qcom,apm-ctrl", NULL)) { | 
|  | 1734 | /* No APM used */ | 
|  | 1735 | return 0; | 
|  | 1736 | } | 
|  | 1737 |  | 
|  | 1738 | ctrl->apm = msm_apm_ctrl_dev_get(ctrl->dev); | 
|  | 1739 | if (IS_ERR(ctrl->apm)) { | 
|  | 1740 | rc = PTR_ERR(ctrl->apm); | 
|  | 1741 | if (rc != -EPROBE_DEFER) | 
|  | 1742 | cpr3_err(ctrl, "APM get failed, rc=%d\n", rc); | 
|  | 1743 | return rc; | 
|  | 1744 | } | 
|  | 1745 |  | 
|  | 1746 | rc = of_property_read_u32(node, "qcom,apm-threshold-voltage", | 
|  | 1747 | &ctrl->apm_threshold_volt); | 
|  | 1748 | if (rc) { | 
|  | 1749 | cpr3_err(ctrl, "error reading qcom,apm-threshold-voltage, rc=%d\n", | 
|  | 1750 | rc); | 
|  | 1751 | return rc; | 
|  | 1752 | } | 
|  | 1753 | ctrl->apm_threshold_volt | 
|  | 1754 | = CPR3_ROUND(ctrl->apm_threshold_volt, ctrl->step_volt); | 
|  | 1755 |  | 
|  | 1756 | /* No error check since this is an optional property. */ | 
|  | 1757 | of_property_read_u32(node, "qcom,apm-hysteresis-voltage", | 
|  | 1758 | &ctrl->apm_adj_volt); | 
|  | 1759 | ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); | 
|  | 1760 |  | 
|  | 1761 | ctrl->apm_high_supply = MSM_APM_SUPPLY_APCC; | 
|  | 1762 | ctrl->apm_low_supply = MSM_APM_SUPPLY_MX; | 
|  | 1763 |  | 
|  | 1764 | return 0; | 
|  | 1765 | } | 
|  | 1766 |  | 
|  | 1767 | /** | 
|  | 1768 | * cpr3_mem_acc_init() - initialize mem-acc regulator data for | 
|  | 1769 | *		a CPR3 regulator | 
|  | 1770 | * @ctrl:		Pointer to the CPR3 controller | 
|  | 1771 | * | 
|  | 1772 | * Return: 0 on success, errno on failure | 
|  | 1773 | */ | 
|  | 1774 | int cpr3_mem_acc_init(struct cpr3_regulator *vreg) | 
|  | 1775 | { | 
|  | 1776 | struct cpr3_controller *ctrl = vreg->thread->ctrl; | 
|  | 1777 | u32 *temp; | 
|  | 1778 | int i, rc; | 
|  | 1779 |  | 
|  | 1780 | if (!ctrl->mem_acc_regulator) { | 
|  | 1781 | cpr3_info(ctrl, "not using memory accelerator regulator\n"); | 
|  | 1782 | return 0; | 
|  | 1783 | } | 
|  | 1784 |  | 
|  | 1785 | temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); | 
|  | 1786 | if (!temp) | 
|  | 1787 | return -ENOMEM; | 
|  | 1788 |  | 
|  | 1789 | rc = cpr3_parse_corner_array_property(vreg, "qcom,mem-acc-voltage", | 
|  | 1790 | 1, temp); | 
|  | 1791 | if (rc) { | 
|  | 1792 | cpr3_err(ctrl, "could not load mem-acc corners, rc=%d\n", rc); | 
|  | 1793 | } else { | 
|  | 1794 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 1795 | vreg->corner[i].mem_acc_volt = temp[i]; | 
|  | 1796 | } | 
|  | 1797 |  | 
|  | 1798 | kfree(temp); | 
|  | 1799 | return rc; | 
|  | 1800 | } | 
|  | 1801 |  | 
|  | 1802 | /** | 
|  | 1803 | * cpr4_load_core_and_temp_adj() - parse amount of voltage adjustment for | 
|  | 1804 | *		per-online-core and per-temperature voltage adjustment for a | 
|  | 1805 | *		given corner or corner band from device tree. | 
|  | 1806 | * @vreg:	Pointer to the CPR3 regulator | 
|  | 1807 | * @num:	Corner number or corner band number | 
|  | 1808 | * @use_corner_band:	Boolean indicating if the CPR3 regulator supports | 
|  | 1809 | *			adjustments per corner band | 
|  | 1810 | * | 
|  | 1811 | * Return: 0 on success, errno on failure | 
|  | 1812 | */ | 
|  | 1813 | static int cpr4_load_core_and_temp_adj(struct cpr3_regulator *vreg, | 
|  | 1814 | int num, bool use_corner_band) | 
|  | 1815 | { | 
|  | 1816 | struct cpr3_controller *ctrl = vreg->thread->ctrl; | 
|  | 1817 | struct cpr4_sdelta *sdelta; | 
|  | 1818 | int sdelta_size, i, j, pos, rc = 0; | 
|  | 1819 | char str[75]; | 
|  | 1820 | size_t buflen; | 
|  | 1821 | char *buf; | 
|  | 1822 |  | 
|  | 1823 | sdelta = use_corner_band ? vreg->corner_band[num].sdelta : | 
|  | 1824 | vreg->corner[num].sdelta; | 
|  | 1825 |  | 
|  | 1826 | if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) { | 
|  | 1827 | /* corner doesn't need sdelta table */ | 
|  | 1828 | sdelta->max_core_count = 0; | 
|  | 1829 | sdelta->temp_band_count = 0; | 
|  | 1830 | return rc; | 
|  | 1831 | } | 
|  | 1832 |  | 
|  | 1833 | sdelta_size = sdelta->max_core_count * sdelta->temp_band_count; | 
|  | 1834 | snprintf(str, sizeof(str), use_corner_band ? | 
|  | 1835 | "corner_band=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n" | 
|  | 1836 | : "corner=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", | 
|  | 1837 | num, sdelta->max_core_count, | 
|  | 1838 | sdelta->temp_band_count, sdelta_size); | 
|  | 1839 |  | 
|  | 1840 | cpr3_debug(vreg, "%s", str); | 
|  | 1841 |  | 
|  | 1842 | sdelta->table = devm_kcalloc(ctrl->dev, sdelta_size, | 
|  | 1843 | sizeof(*sdelta->table), GFP_KERNEL); | 
|  | 1844 | if (!sdelta->table) | 
|  | 1845 | return -ENOMEM; | 
|  | 1846 |  | 
|  | 1847 | snprintf(str, sizeof(str), use_corner_band ? | 
|  | 1848 | "qcom,cpr-corner-band%d-temp-core-voltage-adjustment" : | 
|  | 1849 | "qcom,cpr-corner%d-temp-core-voltage-adjustment", | 
|  | 1850 | num + CPR3_CORNER_OFFSET); | 
|  | 1851 |  | 
|  | 1852 | rc = cpr3_parse_array_property(vreg, str, sdelta_size, | 
|  | 1853 | sdelta->table); | 
|  | 1854 | if (rc) { | 
|  | 1855 | cpr3_err(vreg, "could not load %s, rc=%d\n", str, rc); | 
|  | 1856 | return rc; | 
|  | 1857 | } | 
|  | 1858 |  | 
|  | 1859 | /* | 
|  | 1860 | * Convert sdelta margins from uV to PMIC steps and apply negation to | 
|  | 1861 | * follow the SDELTA register semantics. | 
|  | 1862 | */ | 
|  | 1863 | for (i = 0; i < sdelta_size; i++) | 
|  | 1864 | sdelta->table[i] = -(sdelta->table[i] / ctrl->step_volt); | 
|  | 1865 |  | 
|  | 1866 | buflen = sizeof(*buf) * sdelta_size * (MAX_CHARS_PER_INT + 2); | 
|  | 1867 | buf = kzalloc(buflen, GFP_KERNEL); | 
|  | 1868 | if (!buf) | 
|  | 1869 | return rc; | 
|  | 1870 |  | 
|  | 1871 | for (i = 0; i < sdelta->max_core_count; i++) { | 
|  | 1872 | for (j = 0, pos = 0; j < sdelta->temp_band_count; j++) | 
|  | 1873 | pos += scnprintf(buf + pos, buflen - pos, " %u", | 
|  | 1874 | sdelta->table[i * sdelta->temp_band_count + j]); | 
|  | 1875 | cpr3_debug(vreg, "sdelta[%d]:%s\n", i, buf); | 
|  | 1876 | } | 
|  | 1877 |  | 
|  | 1878 | kfree(buf); | 
|  | 1879 | return rc; | 
|  | 1880 | } | 
|  | 1881 |  | 
|  | 1882 | /** | 
|  | 1883 | * cpr4_parse_core_count_temp_voltage_adj() - parse configuration data for | 
|  | 1884 | *		per-online-core and per-temperature voltage adjustment for | 
|  | 1885 | *		a CPR3 regulator from device tree. | 
|  | 1886 | * @vreg:	Pointer to the CPR3 regulator | 
|  | 1887 | * @use_corner_band:	Boolean indicating if the CPR3 regulator supports | 
|  | 1888 | *			adjustments per corner band | 
|  | 1889 | * | 
|  | 1890 | * This function supports parsing of per-online-core and per-temperature | 
|  | 1891 | * adjustments per corner or per corner band. CPR controllers which support | 
|  | 1892 | * corner bands apply the same adjustments to all corners within a corner band. | 
|  | 1893 | * | 
|  | 1894 | * Return: 0 on success, errno on failure | 
|  | 1895 | */ | 
|  | 1896 | int cpr4_parse_core_count_temp_voltage_adj( | 
|  | 1897 | struct cpr3_regulator *vreg, bool use_corner_band) | 
|  | 1898 | { | 
|  | 1899 | struct cpr3_controller *ctrl = vreg->thread->ctrl; | 
|  | 1900 | struct device_node *node = vreg->of_node; | 
|  | 1901 | struct cpr3_corner *corner; | 
|  | 1902 | struct cpr4_sdelta *sdelta; | 
|  | 1903 | int i, sdelta_table_count, rc = 0; | 
|  | 1904 | int *allow_core_count_adj = NULL, *allow_temp_adj = NULL; | 
|  | 1905 | char prop_str[75]; | 
|  | 1906 |  | 
|  | 1907 | if (of_find_property(node, use_corner_band ? | 
|  | 1908 | "qcom,corner-band-allow-temp-adjustment" | 
|  | 1909 | : "qcom,corner-allow-temp-adjustment", NULL)) { | 
|  | 1910 | if (!ctrl->allow_temp_adj) { | 
|  | 1911 | cpr3_err(ctrl, "Temperature adjustment configurations missing\n"); | 
|  | 1912 | return -EINVAL; | 
|  | 1913 | } | 
|  | 1914 |  | 
|  | 1915 | vreg->allow_temp_adj = true; | 
|  | 1916 | } | 
|  | 1917 |  | 
|  | 1918 | if (of_find_property(node, use_corner_band ? | 
|  | 1919 | "qcom,corner-band-allow-core-count-adjustment" | 
|  | 1920 | : "qcom,corner-allow-core-count-adjustment", | 
|  | 1921 | NULL)) { | 
|  | 1922 | rc = of_property_read_u32(node, "qcom,max-core-count", | 
|  | 1923 | &vreg->max_core_count); | 
|  | 1924 | if (rc) { | 
|  | 1925 | cpr3_err(vreg, "error reading qcom,max-core-count, rc=%d\n", | 
|  | 1926 | rc); | 
|  | 1927 | return -EINVAL; | 
|  | 1928 | } | 
|  | 1929 |  | 
|  | 1930 | vreg->allow_core_count_adj = true; | 
|  | 1931 | ctrl->allow_core_count_adj = true; | 
|  | 1932 | } | 
|  | 1933 |  | 
|  | 1934 | if (!vreg->allow_temp_adj && !vreg->allow_core_count_adj) { | 
|  | 1935 | /* | 
|  | 1936 | * Both per-online-core and temperature based adjustments are | 
|  | 1937 | * disabled for this regulator. | 
|  | 1938 | */ | 
|  | 1939 | return 0; | 
|  | 1940 | } else if (!vreg->allow_core_count_adj) { | 
|  | 1941 | /* | 
|  | 1942 | * Only per-temperature voltage adjusments are allowed. | 
|  | 1943 | * Keep max core count value as 1 to allocate SDELTA. | 
|  | 1944 | */ | 
|  | 1945 | vreg->max_core_count = 1; | 
|  | 1946 | } | 
|  | 1947 |  | 
|  | 1948 | if (vreg->allow_core_count_adj) { | 
|  | 1949 | allow_core_count_adj = kcalloc(vreg->corner_count, | 
|  | 1950 | sizeof(*allow_core_count_adj), | 
|  | 1951 | GFP_KERNEL); | 
|  | 1952 | if (!allow_core_count_adj) | 
|  | 1953 | return -ENOMEM; | 
|  | 1954 |  | 
|  | 1955 | snprintf(prop_str, sizeof(prop_str), use_corner_band ? | 
|  | 1956 | "qcom,corner-band-allow-core-count-adjustment" : | 
|  | 1957 | "qcom,corner-allow-core-count-adjustment"); | 
|  | 1958 |  | 
|  | 1959 | rc = use_corner_band ? | 
|  | 1960 | cpr3_parse_corner_band_array_property(vreg, prop_str, | 
|  | 1961 | 1, allow_core_count_adj) : | 
|  | 1962 | cpr3_parse_corner_array_property(vreg, prop_str, | 
|  | 1963 | 1, allow_core_count_adj); | 
|  | 1964 | if (rc) { | 
|  | 1965 | cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, | 
|  | 1966 | rc); | 
|  | 1967 | goto done; | 
|  | 1968 | } | 
|  | 1969 | } | 
|  | 1970 |  | 
|  | 1971 | if (vreg->allow_temp_adj) { | 
|  | 1972 | allow_temp_adj = kcalloc(vreg->corner_count, | 
|  | 1973 | sizeof(*allow_temp_adj), GFP_KERNEL); | 
|  | 1974 | if (!allow_temp_adj) { | 
|  | 1975 | rc = -ENOMEM; | 
|  | 1976 | goto done; | 
|  | 1977 | } | 
|  | 1978 |  | 
|  | 1979 | snprintf(prop_str, sizeof(prop_str), use_corner_band ? | 
|  | 1980 | "qcom,corner-band-allow-temp-adjustment" : | 
|  | 1981 | "qcom,corner-allow-temp-adjustment"); | 
|  | 1982 |  | 
|  | 1983 | rc = use_corner_band ? | 
|  | 1984 | cpr3_parse_corner_band_array_property(vreg, prop_str, | 
|  | 1985 | 1, allow_temp_adj) : | 
|  | 1986 | cpr3_parse_corner_array_property(vreg, prop_str, | 
|  | 1987 | 1, allow_temp_adj); | 
|  | 1988 | if (rc) { | 
|  | 1989 | cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, | 
|  | 1990 | rc); | 
|  | 1991 | goto done; | 
|  | 1992 | } | 
|  | 1993 | } | 
|  | 1994 |  | 
|  | 1995 | sdelta_table_count = use_corner_band ? vreg->corner_band_count : | 
|  | 1996 | vreg->corner_count; | 
|  | 1997 |  | 
|  | 1998 | for (i = 0; i < sdelta_table_count; i++) { | 
|  | 1999 | sdelta = devm_kzalloc(ctrl->dev, sizeof(*corner->sdelta), | 
|  | 2000 | GFP_KERNEL); | 
|  | 2001 | if (!sdelta) { | 
|  | 2002 | rc = -ENOMEM; | 
|  | 2003 | goto done; | 
|  | 2004 | } | 
|  | 2005 |  | 
|  | 2006 | if (allow_core_count_adj) | 
|  | 2007 | sdelta->allow_core_count_adj = allow_core_count_adj[i]; | 
|  | 2008 | if (allow_temp_adj) | 
|  | 2009 | sdelta->allow_temp_adj = allow_temp_adj[i]; | 
|  | 2010 | sdelta->max_core_count = vreg->max_core_count; | 
|  | 2011 | sdelta->temp_band_count = ctrl->temp_band_count; | 
|  | 2012 |  | 
|  | 2013 | if (use_corner_band) | 
|  | 2014 | vreg->corner_band[i].sdelta = sdelta; | 
|  | 2015 | else | 
|  | 2016 | vreg->corner[i].sdelta = sdelta; | 
|  | 2017 |  | 
|  | 2018 | rc = cpr4_load_core_and_temp_adj(vreg, i, use_corner_band); | 
|  | 2019 | if (rc) { | 
|  | 2020 | cpr3_err(vreg, "corner/band %d core and temp adjustment loading failed, rc=%d\n", | 
|  | 2021 | i, rc); | 
|  | 2022 | goto done; | 
|  | 2023 | } | 
|  | 2024 | } | 
|  | 2025 |  | 
|  | 2026 | done: | 
|  | 2027 | kfree(allow_core_count_adj); | 
|  | 2028 | kfree(allow_temp_adj); | 
|  | 2029 |  | 
|  | 2030 | return rc; | 
|  | 2031 | } | 
|  | 2032 |  | 
|  | 2033 | /** | 
|  | 2034 | * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages | 
|  | 2035 | *		so that they do not overlap the APM threshold voltage. | 
|  | 2036 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2037 | * | 
|  | 2038 | * The memory array power mux (APM) must be configured for a specific supply | 
|  | 2039 | * based upon where the VDD voltage lies with respect to the APM threshold | 
|  | 2040 | * voltage.  When using CPR hardware closed-loop, the voltage may vary anywhere | 
|  | 2041 | * between the floor and ceiling voltage without software notification. | 
|  | 2042 | * Therefore, it is required that the floor to ceiling range for every corner | 
|  | 2043 | * not intersect the APM threshold voltage.  This function adjusts the floor to | 
|  | 2044 | * ceiling range for each corner which violates this requirement. | 
|  | 2045 | * | 
|  | 2046 | * The following algorithm is applied: | 
|  | 2047 | *	if floor < threshold <= ceiling: | 
|  | 2048 | *		if open_loop >= threshold, then floor = threshold - adj | 
|  | 2049 | *		else ceiling = threshold - step | 
|  | 2050 | * where: | 
|  | 2051 | *	adj = APM hysteresis voltage established to minimize the number of | 
|  | 2052 | *	      corners with artificially increased floor voltages | 
|  | 2053 | *	step = voltage in microvolts of a single step of the VDD supply | 
|  | 2054 | * | 
|  | 2055 | * The open-loop voltage is also bounded by the new floor or ceiling value as | 
|  | 2056 | * needed. | 
|  | 2057 | * | 
|  | 2058 | * Return: none | 
|  | 2059 | */ | 
|  | 2060 | void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) | 
|  | 2061 | { | 
|  | 2062 | struct cpr3_controller *ctrl = vreg->thread->ctrl; | 
|  | 2063 | struct cpr3_corner *corner; | 
|  | 2064 | int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop; | 
|  | 2065 |  | 
|  | 2066 | if (!ctrl->apm_threshold_volt) { | 
|  | 2067 | /* APM not being used. */ | 
|  | 2068 | return; | 
|  | 2069 | } | 
|  | 2070 |  | 
|  | 2071 | ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt, | 
|  | 2072 | ctrl->step_volt); | 
|  | 2073 | ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); | 
|  | 2074 |  | 
|  | 2075 | threshold = ctrl->apm_threshold_volt; | 
|  | 2076 | adj = ctrl->apm_adj_volt; | 
|  | 2077 |  | 
|  | 2078 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 2079 | corner = &vreg->corner[i]; | 
|  | 2080 |  | 
|  | 2081 | if (threshold <= corner->floor_volt | 
|  | 2082 | || threshold > corner->ceiling_volt) | 
|  | 2083 | continue; | 
|  | 2084 |  | 
|  | 2085 | prev_floor = corner->floor_volt; | 
|  | 2086 | prev_ceiling = corner->ceiling_volt; | 
|  | 2087 | prev_open_loop = corner->open_loop_volt; | 
|  | 2088 |  | 
|  | 2089 | if (corner->open_loop_volt >= threshold) { | 
|  | 2090 | corner->floor_volt = max(corner->floor_volt, | 
|  | 2091 | threshold - adj); | 
|  | 2092 | if (corner->open_loop_volt < corner->floor_volt) | 
|  | 2093 | corner->open_loop_volt = corner->floor_volt; | 
|  | 2094 | } else { | 
|  | 2095 | corner->ceiling_volt = threshold - ctrl->step_volt; | 
|  | 2096 | } | 
|  | 2097 |  | 
|  | 2098 | if (corner->floor_volt != prev_floor | 
|  | 2099 | || corner->ceiling_volt != prev_ceiling | 
|  | 2100 | || corner->open_loop_volt != prev_open_loop) | 
|  | 2101 | cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", | 
|  | 2102 | threshold, adj, i, prev_floor, prev_ceiling, | 
|  | 2103 | prev_open_loop, corner->floor_volt, | 
|  | 2104 | corner->ceiling_volt, corner->open_loop_volt); | 
|  | 2105 | } | 
|  | 2106 | } | 
|  | 2107 |  | 
|  | 2108 | /** | 
|  | 2109 | * cprh_adjust_voltages_for_mem_acc() - adjust per-corner floor and ceiling | 
|  | 2110 | *		voltages so that they do not intersect the MEM ACC threshold | 
|  | 2111 | *		voltage | 
|  | 2112 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2113 | * | 
|  | 2114 | * The following algorithm is applied: | 
|  | 2115 | *	if floor < threshold <= ceiling: | 
|  | 2116 | *		if open_loop >= threshold, then floor = threshold | 
|  | 2117 | *		else ceiling = threshold - step | 
|  | 2118 | * where: | 
|  | 2119 | *	step = voltage in microvolts of a single step of the VDD supply | 
|  | 2120 | * | 
|  | 2121 | * The open-loop voltage is also bounded by the new floor or ceiling value as | 
|  | 2122 | * needed. | 
|  | 2123 | * | 
|  | 2124 | * Return: none | 
|  | 2125 | */ | 
|  | 2126 | void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) | 
|  | 2127 | { | 
|  | 2128 | struct cpr3_controller *ctrl = vreg->thread->ctrl; | 
|  | 2129 | struct cpr3_corner *corner; | 
|  | 2130 | int i, threshold, prev_ceiling, prev_floor, prev_open_loop; | 
|  | 2131 |  | 
|  | 2132 | if (!ctrl->mem_acc_threshold_volt) { | 
|  | 2133 | /* MEM ACC not being used. */ | 
|  | 2134 | return; | 
|  | 2135 | } | 
|  | 2136 |  | 
|  | 2137 | ctrl->mem_acc_threshold_volt = CPR3_ROUND(ctrl->mem_acc_threshold_volt, | 
|  | 2138 | ctrl->step_volt); | 
|  | 2139 |  | 
|  | 2140 | threshold = ctrl->mem_acc_threshold_volt; | 
|  | 2141 |  | 
|  | 2142 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 2143 | corner = &vreg->corner[i]; | 
|  | 2144 |  | 
|  | 2145 | if (threshold <= corner->floor_volt | 
|  | 2146 | || threshold > corner->ceiling_volt) | 
|  | 2147 | continue; | 
|  | 2148 |  | 
|  | 2149 | prev_floor = corner->floor_volt; | 
|  | 2150 | prev_ceiling = corner->ceiling_volt; | 
|  | 2151 | prev_open_loop = corner->open_loop_volt; | 
|  | 2152 |  | 
|  | 2153 | if (corner->open_loop_volt >= threshold) { | 
|  | 2154 | corner->floor_volt = max(corner->floor_volt, threshold); | 
|  | 2155 | if (corner->open_loop_volt < corner->floor_volt) | 
|  | 2156 | corner->open_loop_volt = corner->floor_volt; | 
|  | 2157 | } else { | 
|  | 2158 | corner->ceiling_volt = threshold - ctrl->step_volt; | 
|  | 2159 | } | 
|  | 2160 |  | 
|  | 2161 | if (corner->floor_volt != prev_floor | 
|  | 2162 | || corner->ceiling_volt != prev_ceiling | 
|  | 2163 | || corner->open_loop_volt != prev_open_loop) | 
|  | 2164 | cpr3_debug(vreg, "MEM ACC threshold=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", | 
|  | 2165 | threshold, i, prev_floor, prev_ceiling, | 
|  | 2166 | prev_open_loop, corner->floor_volt, | 
|  | 2167 | corner->ceiling_volt, corner->open_loop_volt); | 
|  | 2168 | } | 
|  | 2169 | } | 
|  | 2170 |  | 
|  | 2171 | /** | 
|  | 2172 | * cpr3_apply_closed_loop_offset_voltages() - modify the closed-loop voltage | 
|  | 2173 | *		adjustments by the amounts that are needed for this | 
|  | 2174 | *		fuse combo | 
|  | 2175 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2176 | * @volt_adjust:	Array of closed-loop voltage adjustment values of length | 
|  | 2177 | *			vreg->corner_count which is further adjusted based upon | 
|  | 2178 | *			offset voltage fuse values. | 
|  | 2179 | * @fuse_volt_adjust:	Fused closed-loop voltage adjustment values of length | 
|  | 2180 | *			vreg->fuse_corner_count. | 
|  | 2181 | * | 
|  | 2182 | * Return: 0 on success, errno on failure | 
|  | 2183 | */ | 
|  | 2184 | static int cpr3_apply_closed_loop_offset_voltages(struct cpr3_regulator *vreg, | 
|  | 2185 | int *volt_adjust, int *fuse_volt_adjust) | 
|  | 2186 | { | 
|  | 2187 | u32 *corner_map; | 
|  | 2188 | int rc = 0, i; | 
|  | 2189 |  | 
|  | 2190 | if (!of_find_property(vreg->of_node, | 
|  | 2191 | "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { | 
|  | 2192 | /* No closed-loop offset required. */ | 
|  | 2193 | return 0; | 
|  | 2194 | } | 
|  | 2195 |  | 
|  | 2196 | corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map), | 
|  | 2197 | GFP_KERNEL); | 
|  | 2198 | if (!corner_map) | 
|  | 2199 | return -ENOMEM; | 
|  | 2200 |  | 
|  | 2201 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 2202 | "qcom,cpr-fused-closed-loop-voltage-adjustment-map", | 
|  | 2203 | 1, corner_map); | 
|  | 2204 | if (rc) | 
|  | 2205 | goto done; | 
|  | 2206 |  | 
|  | 2207 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 2208 | if (corner_map[i] == 0) { | 
|  | 2209 | continue; | 
|  | 2210 | } else if (corner_map[i] > vreg->fuse_corner_count) { | 
|  | 2211 | cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n", | 
|  | 2212 | i, corner_map[i]); | 
|  | 2213 | rc = -EINVAL; | 
|  | 2214 | goto done; | 
|  | 2215 | } | 
|  | 2216 |  | 
|  | 2217 | volt_adjust[i] += fuse_volt_adjust[corner_map[i] - 1]; | 
|  | 2218 | } | 
|  | 2219 |  | 
|  | 2220 | done: | 
|  | 2221 | kfree(corner_map); | 
|  | 2222 | return rc; | 
|  | 2223 | } | 
|  | 2224 |  | 
|  | 2225 | /** | 
|  | 2226 | * cpr3_enforce_inc_quotient_monotonicity() - Ensure that target quotients | 
|  | 2227 | *		increase monotonically from lower to higher corners | 
|  | 2228 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2229 | * | 
|  | 2230 | * Return: 0 on success, errno on failure | 
|  | 2231 | */ | 
|  | 2232 | static void cpr3_enforce_inc_quotient_monotonicity(struct cpr3_regulator *vreg) | 
|  | 2233 | { | 
|  | 2234 | int i, j; | 
|  | 2235 |  | 
|  | 2236 | for (i = 1; i < vreg->corner_count; i++) { | 
|  | 2237 | for (j = 0; j < CPR3_RO_COUNT; j++) { | 
|  | 2238 | if (vreg->corner[i].target_quot[j] | 
|  | 2239 | && vreg->corner[i].target_quot[j] | 
|  | 2240 | < vreg->corner[i - 1].target_quot[j]) { | 
|  | 2241 | cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", | 
|  | 2242 | i, j, | 
|  | 2243 | vreg->corner[i].target_quot[j], | 
|  | 2244 | i - 1, j, | 
|  | 2245 | vreg->corner[i - 1].target_quot[j], | 
|  | 2246 | i, j, | 
|  | 2247 | vreg->corner[i - 1].target_quot[j]); | 
|  | 2248 | vreg->corner[i].target_quot[j] | 
|  | 2249 | = vreg->corner[i - 1].target_quot[j]; | 
|  | 2250 | } | 
|  | 2251 | } | 
|  | 2252 | } | 
|  | 2253 | } | 
|  | 2254 |  | 
|  | 2255 | /** | 
|  | 2256 | * cpr3_enforce_dec_quotient_monotonicity() - Ensure that target quotients | 
|  | 2257 | *		decrease monotonically from higher to lower corners | 
|  | 2258 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2259 | * | 
|  | 2260 | * Return: 0 on success, errno on failure | 
|  | 2261 | */ | 
|  | 2262 | static void cpr3_enforce_dec_quotient_monotonicity(struct cpr3_regulator *vreg) | 
|  | 2263 | { | 
|  | 2264 | int i, j; | 
|  | 2265 |  | 
|  | 2266 | for (i = vreg->corner_count - 2; i >= 0; i--) { | 
|  | 2267 | for (j = 0; j < CPR3_RO_COUNT; j++) { | 
|  | 2268 | if (vreg->corner[i + 1].target_quot[j] | 
|  | 2269 | && vreg->corner[i].target_quot[j] | 
|  | 2270 | > vreg->corner[i + 1].target_quot[j]) { | 
|  | 2271 | cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", | 
|  | 2272 | i, j, | 
|  | 2273 | vreg->corner[i].target_quot[j], | 
|  | 2274 | i + 1, j, | 
|  | 2275 | vreg->corner[i + 1].target_quot[j], | 
|  | 2276 | i, j, | 
|  | 2277 | vreg->corner[i + 1].target_quot[j]); | 
|  | 2278 | vreg->corner[i].target_quot[j] | 
|  | 2279 | = vreg->corner[i + 1].target_quot[j]; | 
|  | 2280 | } | 
|  | 2281 | } | 
|  | 2282 | } | 
|  | 2283 | } | 
|  | 2284 |  | 
|  | 2285 | /** | 
|  | 2286 | * _cpr3_adjust_target_quotients() - adjust the target quotients for each | 
|  | 2287 | *		corner of the regulator according to input adjustment and | 
|  | 2288 | *		scaling arrays | 
|  | 2289 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2290 | * @volt_adjust:	Pointer to an array of closed-loop voltage adjustments | 
|  | 2291 | *			with units of microvolts.  The array must have | 
|  | 2292 | *			vreg->corner_count number of elements. | 
|  | 2293 | * @ro_scale:		Pointer to a flattened 2D array of RO scaling factors. | 
|  | 2294 | *			The array must have an inner dimension of CPR3_RO_COUNT | 
|  | 2295 | *			and an outer dimension of vreg->corner_count | 
|  | 2296 | * @label:		Null terminated string providing a label for the type | 
|  | 2297 | *			of adjustment. | 
|  | 2298 | * | 
|  | 2299 | * Return: true if any corners received a positive voltage adjustment (> 0), | 
|  | 2300 | *	   else false | 
|  | 2301 | */ | 
|  | 2302 | static bool _cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, | 
|  | 2303 | const int *volt_adjust, const int *ro_scale, const char *label) | 
|  | 2304 | { | 
|  | 2305 | int i, j, quot_adjust; | 
|  | 2306 | bool is_increasing = false; | 
|  | 2307 | u32 prev_quot; | 
|  | 2308 |  | 
|  | 2309 | for (i = 0; i < vreg->corner_count; i++) { | 
|  | 2310 | for (j = 0; j < CPR3_RO_COUNT; j++) { | 
|  | 2311 | if (vreg->corner[i].target_quot[j]) { | 
|  | 2312 | quot_adjust = cpr3_quot_adjustment( | 
|  | 2313 | ro_scale[i * CPR3_RO_COUNT + j], | 
|  | 2314 | volt_adjust[i]); | 
|  | 2315 | if (quot_adjust) { | 
|  | 2316 | prev_quot = vreg->corner[i]. | 
|  | 2317 | target_quot[j]; | 
|  | 2318 | vreg->corner[i].target_quot[j] | 
|  | 2319 | += quot_adjust; | 
|  | 2320 | cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n", | 
|  | 2321 | i, j, label, prev_quot, | 
|  | 2322 | vreg->corner[i].target_quot[j], | 
|  | 2323 | volt_adjust[i]); | 
|  | 2324 | } | 
|  | 2325 | } | 
|  | 2326 | } | 
|  | 2327 | if (volt_adjust[i] > 0) | 
|  | 2328 | is_increasing = true; | 
|  | 2329 | } | 
|  | 2330 |  | 
|  | 2331 | return is_increasing; | 
|  | 2332 | } | 
|  | 2333 |  | 
|  | 2334 | /** | 
|  | 2335 | * cpr3_adjust_target_quotients() - adjust the target quotients for each | 
|  | 2336 | *			corner according to device tree values and fuse values | 
|  | 2337 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2338 | * @fuse_volt_adjust:	Fused closed-loop voltage adjustment values of length | 
|  | 2339 | *			vreg->fuse_corner_count. This parameter could be null | 
|  | 2340 | *			pointer when no fused adjustments are needed. | 
|  | 2341 | * | 
|  | 2342 | * Return: 0 on success, errno on failure | 
|  | 2343 | */ | 
|  | 2344 | int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, | 
|  | 2345 | int *fuse_volt_adjust) | 
|  | 2346 | { | 
|  | 2347 | int i, rc; | 
|  | 2348 | int *volt_adjust, *ro_scale; | 
|  | 2349 | bool explicit_adjustment, fused_adjustment, is_increasing; | 
|  | 2350 |  | 
|  | 2351 | explicit_adjustment = of_find_property(vreg->of_node, | 
|  | 2352 | "qcom,cpr-closed-loop-voltage-adjustment", NULL); | 
|  | 2353 | fused_adjustment = of_find_property(vreg->of_node, | 
|  | 2354 | "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); | 
|  | 2355 |  | 
|  | 2356 | if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { | 
|  | 2357 | /* No adjustment required. */ | 
|  | 2358 | return 0; | 
|  | 2359 | } else if (!of_find_property(vreg->of_node, | 
|  | 2360 | "qcom,cpr-ro-scaling-factor", NULL)) { | 
|  | 2361 | cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n"); | 
|  | 2362 | return -EINVAL; | 
|  | 2363 | } | 
|  | 2364 |  | 
|  | 2365 | volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), | 
|  | 2366 | GFP_KERNEL); | 
|  | 2367 | ro_scale = kcalloc(vreg->corner_count * CPR3_RO_COUNT, | 
|  | 2368 | sizeof(*ro_scale), GFP_KERNEL); | 
|  | 2369 | if (!volt_adjust || !ro_scale) { | 
|  | 2370 | rc = -ENOMEM; | 
|  | 2371 | goto done; | 
|  | 2372 | } | 
|  | 2373 |  | 
|  | 2374 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 2375 | "qcom,cpr-ro-scaling-factor", CPR3_RO_COUNT, ro_scale); | 
|  | 2376 | if (rc) { | 
|  | 2377 | cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", | 
|  | 2378 | rc); | 
|  | 2379 | goto done; | 
|  | 2380 | } | 
|  | 2381 |  | 
|  | 2382 | for (i = 0; i < vreg->corner_count; i++) | 
|  | 2383 | memcpy(vreg->corner[i].ro_scale, &ro_scale[i * CPR3_RO_COUNT], | 
|  | 2384 | sizeof(*ro_scale) * CPR3_RO_COUNT); | 
|  | 2385 |  | 
|  | 2386 | if (explicit_adjustment) { | 
|  | 2387 | rc = cpr3_parse_corner_array_property(vreg, | 
|  | 2388 | "qcom,cpr-closed-loop-voltage-adjustment", | 
|  | 2389 | 1, volt_adjust); | 
|  | 2390 | if (rc) { | 
|  | 2391 | cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", | 
|  | 2392 | rc); | 
|  | 2393 | goto done; | 
|  | 2394 | } | 
|  | 2395 |  | 
|  | 2396 | _cpr3_adjust_target_quotients(vreg, volt_adjust, ro_scale, | 
|  | 2397 | "from DT"); | 
|  | 2398 | cpr3_enforce_inc_quotient_monotonicity(vreg); | 
|  | 2399 | } | 
|  | 2400 |  | 
|  | 2401 | if (fused_adjustment && fuse_volt_adjust) { | 
|  | 2402 | memset(volt_adjust, 0, | 
|  | 2403 | sizeof(*volt_adjust) * vreg->corner_count); | 
|  | 2404 |  | 
|  | 2405 | rc = cpr3_apply_closed_loop_offset_voltages(vreg, volt_adjust, | 
|  | 2406 | fuse_volt_adjust); | 
|  | 2407 | if (rc) { | 
|  | 2408 | cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n", | 
|  | 2409 | rc); | 
|  | 2410 | goto done; | 
|  | 2411 | } | 
|  | 2412 |  | 
|  | 2413 | is_increasing = _cpr3_adjust_target_quotients(vreg, volt_adjust, | 
|  | 2414 | ro_scale, "from fuse"); | 
|  | 2415 | if (is_increasing) | 
|  | 2416 | cpr3_enforce_inc_quotient_monotonicity(vreg); | 
|  | 2417 | else | 
|  | 2418 | cpr3_enforce_dec_quotient_monotonicity(vreg); | 
|  | 2419 | } | 
|  | 2420 |  | 
|  | 2421 | done: | 
|  | 2422 | kfree(volt_adjust); | 
|  | 2423 | kfree(ro_scale); | 
|  | 2424 | return rc; | 
|  | 2425 | } | 
| Tirupathi Reddy | 53d99a0 | 2016-08-08 17:04:23 +0530 | [diff] [blame] | 2426 |  | 
|  | 2427 | /** | 
|  | 2428 | * cpr3_parse_fuse_combo_map() - parse fuse combo map data for a CPR3 regulator | 
|  | 2429 | *		from device tree. | 
|  | 2430 | * @vreg:		Pointer to the CPR3 regulator | 
|  | 2431 | * @fuse_val:		Array of selection fuse parameter values | 
|  | 2432 | * @fuse_count:		Number of selection fuse parameters used in fuse combo | 
|  | 2433 | *			map | 
|  | 2434 | * | 
|  | 2435 | * This function reads the qcom,cpr-fuse-combo-map device tree property and | 
|  | 2436 | * populates the fuse_combo element of CPR3 regulator with the row number of | 
|  | 2437 | * fuse combo map data that matches with the data in fuse_val input array. | 
|  | 2438 | * | 
|  | 2439 | * Return: 0 on success, -ENODEV if qcom,cpr-fuse-combo-map property is not | 
|  | 2440 | *		specified in device node, other errno on failure | 
|  | 2441 | */ | 
|  | 2442 | int cpr3_parse_fuse_combo_map(struct cpr3_regulator *vreg, u64 *fuse_val, | 
|  | 2443 | int fuse_count) | 
|  | 2444 | { | 
|  | 2445 | struct device_node *node = vreg->of_node; | 
|  | 2446 | int i, j, len, num_fuse_combos, row_size, rc = 0; | 
|  | 2447 | u32 *tmp; | 
|  | 2448 |  | 
|  | 2449 | if (!of_find_property(node, "qcom,cpr-fuse-combo-map", &len)) { | 
|  | 2450 | /* property not specified */ | 
|  | 2451 | return -ENODEV; | 
|  | 2452 | } | 
|  | 2453 |  | 
|  | 2454 | row_size = fuse_count * 2; | 
|  | 2455 | if (len == 0 || len % (sizeof(u32) * row_size)) { | 
|  | 2456 | cpr3_err(vreg, "qcom,cpr-fuse-combo-map length=%d is invalid\n", | 
|  | 2457 | len); | 
|  | 2458 | return -EINVAL; | 
|  | 2459 | } | 
|  | 2460 |  | 
|  | 2461 | num_fuse_combos = len / (sizeof(u32) * row_size); | 
|  | 2462 | vreg->fuse_combos_supported = num_fuse_combos; | 
|  | 2463 |  | 
|  | 2464 | tmp = kzalloc(len, GFP_KERNEL); | 
|  | 2465 | if (!tmp) | 
|  | 2466 | return -ENOMEM; | 
|  | 2467 |  | 
|  | 2468 | rc = of_property_read_u32_array(node, "qcom,cpr-fuse-combo-map", | 
|  | 2469 | tmp, num_fuse_combos * row_size); | 
|  | 2470 | if (rc) { | 
|  | 2471 | cpr3_err(vreg, "could not read qcom,cpr-fuse-combo-map, rc=%d\n", | 
|  | 2472 | rc); | 
|  | 2473 | goto done; | 
|  | 2474 | } | 
|  | 2475 |  | 
|  | 2476 | for (i = 0; i < num_fuse_combos; i++) { | 
|  | 2477 | for (j = 0; j < fuse_count; j++) { | 
|  | 2478 | if (tmp[i * row_size + j * 2] > fuse_val[j] | 
|  | 2479 | || tmp[i * row_size + j * 2 + 1] < fuse_val[j]) | 
|  | 2480 | break; | 
|  | 2481 | } | 
|  | 2482 | if (j == fuse_count) { | 
|  | 2483 | vreg->fuse_combo = i; | 
|  | 2484 | break; | 
|  | 2485 | } | 
|  | 2486 | } | 
|  | 2487 |  | 
|  | 2488 | if (i >= num_fuse_combos) { | 
|  | 2489 | cpr3_err(vreg, "No matching CPR fuse combo found!\n"); | 
|  | 2490 | WARN_ON(1); | 
|  | 2491 | rc = -EINVAL; | 
|  | 2492 | goto done; | 
|  | 2493 | } | 
|  | 2494 |  | 
|  | 2495 | done: | 
|  | 2496 | kfree(tmp); | 
|  | 2497 | return rc; | 
|  | 2498 | } |