Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: flash: leds-qcom-flash: Limit LED current based on thermal condition

The flash module has status bits to indicate different thermal
conditions which are called as OTSTx. For each OTSTx status,
there is a recommended total flash current for all channels to
prevent the flash module entering into higher thermal level.
For example, the total flash current should be limited to 1000mA/500mA
respectively when the HW reaches the OTST1/OTST2 thermal level.

Signed-off-by: Fenglin Wu <quic_fenglinw@quicinc.com>
Link: https://lore.kernel.org/r/20240705-qcom_flash_thermal_derating-v3-1-8e2e2783e3a6@quicinc.com
Signed-off-by: Lee Jones <lee@kernel.org>

authored by

Fenglin Wu and committed by
Lee Jones
a0864cf3 10cc4876

+162 -1
+162 -1
drivers/leds/flash/leds-qcom-flash.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. 3 + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 4 */ 5 5 6 6 #include <linux/bitfield.h> ··· 14 14 #include <media/v4l2-flash-led-class.h> 15 15 16 16 /* registers definitions */ 17 + #define FLASH_REVISION_REG 0x00 18 + #define FLASH_4CH_REVISION_V0P1 0x01 19 + 17 20 #define FLASH_TYPE_REG 0x04 18 21 #define FLASH_TYPE_VAL 0x18 19 22 ··· 76 73 77 74 #define UA_PER_MA 1000 78 75 76 + /* thermal threshold constants */ 77 + #define OTST_3CH_MIN_VAL 3 78 + #define OTST1_4CH_MIN_VAL 0 79 + #define OTST1_4CH_V0P1_MIN_VAL 3 80 + #define OTST2_4CH_MIN_VAL 0 81 + 82 + #define OTST1_MAX_CURRENT_MA 1000 83 + #define OTST2_MAX_CURRENT_MA 500 84 + #define OTST3_MAX_CURRENT_MA 200 85 + 79 86 enum hw_type { 80 87 QCOM_MVFLASH_3CH, 81 88 QCOM_MVFLASH_4CH, ··· 111 98 REG_IRESOLUTION, 112 99 REG_CHAN_STROBE, 113 100 REG_CHAN_EN, 101 + REG_THERM_THRSH1, 102 + REG_THERM_THRSH2, 103 + REG_THERM_THRSH3, 114 104 REG_MAX_COUNT, 115 105 }; 116 106 ··· 127 111 REG_FIELD(0x47, 0, 5), /* iresolution */ 128 112 REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */ 129 113 REG_FIELD(0x4c, 0, 2), /* chan_en */ 114 + REG_FIELD(0x56, 0, 2), /* therm_thrsh1 */ 115 + REG_FIELD(0x57, 0, 2), /* therm_thrsh2 */ 116 + REG_FIELD(0x58, 0, 2), /* therm_thrsh3 */ 130 117 }; 131 118 132 119 static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = { ··· 142 123 REG_FIELD(0x49, 0, 3), /* iresolution */ 143 124 REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */ 144 125 REG_FIELD(0x4e, 0, 3), /* chan_en */ 126 + REG_FIELD(0x7a, 0, 2), /* therm_thrsh1 */ 127 + REG_FIELD(0x78, 0, 2), /* therm_thrsh2 */ 145 128 }; 146 129 147 130 struct qcom_flash_data { ··· 151 130 struct regmap_field *r_fields[REG_MAX_COUNT]; 152 131 struct mutex lock; 153 132 enum hw_type hw_type; 133 + u32 total_ma; 154 134 u8 leds_count; 155 135 u8 max_channels; 156 136 u8 chan_en_bits; 137 + u8 revision; 157 138 }; 158 139 159 140 struct qcom_flash_led { ··· 166 143 u32 max_timeout_ms; 167 144 u32 flash_current_ma; 168 145 u32 flash_timeout_ms; 146 + u32 current_in_use_ma; 169 147 u8 *chan_id; 170 148 u8 chan_count; 171 149 bool enabled; ··· 193 169 dev_err(led->flash.led_cdev.dev, "write module_en failed, rc=%d\n", rc); 194 170 mutex_unlock(&flash_data->lock); 195 171 172 + return rc; 173 + } 174 + 175 + static int update_allowed_flash_current(struct qcom_flash_led *led, u32 *current_ma, bool strobe) 176 + { 177 + struct qcom_flash_data *flash_data = led->flash_data; 178 + u32 therm_ma, avail_ma, thrsh[3], min_thrsh, sts; 179 + int rc = 0; 180 + 181 + mutex_lock(&flash_data->lock); 182 + /* 183 + * Put previously allocated current into allowed budget in either of these two cases: 184 + * 1) LED is disabled; 185 + * 2) LED is enabled repeatedly 186 + */ 187 + if (!strobe || led->current_in_use_ma != 0) { 188 + if (flash_data->total_ma >= led->current_in_use_ma) 189 + flash_data->total_ma -= led->current_in_use_ma; 190 + else 191 + flash_data->total_ma = 0; 192 + 193 + led->current_in_use_ma = 0; 194 + if (!strobe) 195 + goto unlock; 196 + } 197 + 198 + /* 199 + * Cache the default thermal threshold settings, and set them to the lowest levels before 200 + * reading over-temp real time status. If over-temp has been triggered at the lowest 201 + * threshold, it's very likely that it would be triggered at a higher (default) threshold 202 + * when more flash current is requested. Prevent device from triggering over-temp condition 203 + * by limiting the flash current for the new request. 204 + */ 205 + rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH1], &thrsh[0]); 206 + if (rc < 0) 207 + goto unlock; 208 + 209 + rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH2], &thrsh[1]); 210 + if (rc < 0) 211 + goto unlock; 212 + 213 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) { 214 + rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH3], &thrsh[2]); 215 + if (rc < 0) 216 + goto unlock; 217 + } 218 + 219 + min_thrsh = OTST_3CH_MIN_VAL; 220 + if (flash_data->hw_type == QCOM_MVFLASH_4CH) 221 + min_thrsh = (flash_data->revision == FLASH_4CH_REVISION_V0P1) ? 222 + OTST1_4CH_V0P1_MIN_VAL : OTST1_4CH_MIN_VAL; 223 + 224 + rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], min_thrsh); 225 + if (rc < 0) 226 + goto unlock; 227 + 228 + if (flash_data->hw_type == QCOM_MVFLASH_4CH) 229 + min_thrsh = OTST2_4CH_MIN_VAL; 230 + 231 + /* 232 + * The default thermal threshold settings have been updated hence 233 + * restore them if any fault happens starting from here. 234 + */ 235 + rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], min_thrsh); 236 + if (rc < 0) 237 + goto restore; 238 + 239 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) { 240 + rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], min_thrsh); 241 + if (rc < 0) 242 + goto restore; 243 + } 244 + 245 + /* Read thermal level status to get corresponding derating flash current */ 246 + rc = regmap_field_read(flash_data->r_fields[REG_STATUS2], &sts); 247 + if (rc) 248 + goto restore; 249 + 250 + therm_ma = FLASH_TOTAL_CURRENT_MAX_UA / 1000; 251 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) { 252 + if (sts & FLASH_STS_3CH_OTST3) 253 + therm_ma = OTST3_MAX_CURRENT_MA; 254 + else if (sts & FLASH_STS_3CH_OTST2) 255 + therm_ma = OTST2_MAX_CURRENT_MA; 256 + else if (sts & FLASH_STS_3CH_OTST1) 257 + therm_ma = OTST1_MAX_CURRENT_MA; 258 + } else { 259 + if (sts & FLASH_STS_4CH_OTST2) 260 + therm_ma = OTST2_MAX_CURRENT_MA; 261 + else if (sts & FLASH_STS_4CH_OTST1) 262 + therm_ma = OTST1_MAX_CURRENT_MA; 263 + } 264 + 265 + /* Calculate the allowed flash current for the request */ 266 + if (therm_ma <= flash_data->total_ma) 267 + avail_ma = 0; 268 + else 269 + avail_ma = therm_ma - flash_data->total_ma; 270 + 271 + *current_ma = min_t(u32, *current_ma, avail_ma); 272 + led->current_in_use_ma = *current_ma; 273 + flash_data->total_ma += led->current_in_use_ma; 274 + 275 + dev_dbg(led->flash.led_cdev.dev, "allowed flash current: %dmA, total current: %dmA\n", 276 + led->current_in_use_ma, flash_data->total_ma); 277 + 278 + restore: 279 + /* Restore to default thermal threshold settings */ 280 + rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], thrsh[0]); 281 + if (rc < 0) 282 + goto unlock; 283 + 284 + rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], thrsh[1]); 285 + if (rc < 0) 286 + goto unlock; 287 + 288 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) 289 + rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], thrsh[2]); 290 + 291 + unlock: 292 + mutex_unlock(&flash_data->lock); 196 293 return rc; 197 294 } 198 295 ··· 458 313 if (rc) 459 314 return rc; 460 315 316 + rc = update_allowed_flash_current(led, &led->flash_current_ma, state); 317 + if (rc < 0) 318 + return rc; 319 + 461 320 rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE); 462 321 if (rc) 463 322 return rc; ··· 576 427 577 428 rc = set_flash_module_en(led, false); 578 429 if (rc) 430 + return rc; 431 + 432 + rc = update_allowed_flash_current(led, &current_ma, enable); 433 + if (rc < 0) 579 434 return rc; 580 435 581 436 rc = set_flash_current(led, current_ma, TORCH_MODE); ··· 860 707 flash_data->hw_type = QCOM_MVFLASH_4CH; 861 708 flash_data->max_channels = 4; 862 709 regs = mvflash_4ch_regs; 710 + 711 + rc = regmap_read(regmap, reg_base + FLASH_REVISION_REG, &val); 712 + if (rc < 0) { 713 + dev_err(dev, "Failed to read flash LED module revision, rc=%d\n", rc); 714 + return rc; 715 + } 716 + 717 + flash_data->revision = val; 863 718 } else { 864 719 dev_err(dev, "flash LED subtype %#x is not yet supported\n", val); 865 720 return -ENODEV;