Skip to content

Commit a0864cf

Browse files
Fenglin Wulag-linaro
authored andcommitted
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 <[email protected]> Link: https://lore.kernel.org/r/20240705-qcom_flash_thermal_derating-v3-1-8e2e2783e3a6@quicinc.com Signed-off-by: Lee Jones <[email protected]>
1 parent 10cc487 commit a0864cf

File tree

1 file changed

+162
-1
lines changed

1 file changed

+162
-1
lines changed

drivers/leds/flash/leds-qcom-flash.c

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22
/*
3-
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
3+
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
44
*/
55

66
#include <linux/bitfield.h>
@@ -14,6 +14,9 @@
1414
#include <media/v4l2-flash-led-class.h>
1515

1616
/* registers definitions */
17+
#define FLASH_REVISION_REG 0x00
18+
#define FLASH_4CH_REVISION_V0P1 0x01
19+
1720
#define FLASH_TYPE_REG 0x04
1821
#define FLASH_TYPE_VAL 0x18
1922

@@ -73,6 +76,16 @@
7376

7477
#define UA_PER_MA 1000
7578

79+
/* thermal threshold constants */
80+
#define OTST_3CH_MIN_VAL 3
81+
#define OTST1_4CH_MIN_VAL 0
82+
#define OTST1_4CH_V0P1_MIN_VAL 3
83+
#define OTST2_4CH_MIN_VAL 0
84+
85+
#define OTST1_MAX_CURRENT_MA 1000
86+
#define OTST2_MAX_CURRENT_MA 500
87+
#define OTST3_MAX_CURRENT_MA 200
88+
7689
enum hw_type {
7790
QCOM_MVFLASH_3CH,
7891
QCOM_MVFLASH_4CH,
@@ -98,6 +111,9 @@ enum {
98111
REG_IRESOLUTION,
99112
REG_CHAN_STROBE,
100113
REG_CHAN_EN,
114+
REG_THERM_THRSH1,
115+
REG_THERM_THRSH2,
116+
REG_THERM_THRSH3,
101117
REG_MAX_COUNT,
102118
};
103119

@@ -111,6 +127,9 @@ static struct reg_field mvflash_3ch_regs[REG_MAX_COUNT] = {
111127
REG_FIELD(0x47, 0, 5), /* iresolution */
112128
REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */
113129
REG_FIELD(0x4c, 0, 2), /* chan_en */
130+
REG_FIELD(0x56, 0, 2), /* therm_thrsh1 */
131+
REG_FIELD(0x57, 0, 2), /* therm_thrsh2 */
132+
REG_FIELD(0x58, 0, 2), /* therm_thrsh3 */
114133
};
115134

116135
static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
@@ -123,16 +142,20 @@ static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
123142
REG_FIELD(0x49, 0, 3), /* iresolution */
124143
REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */
125144
REG_FIELD(0x4e, 0, 3), /* chan_en */
145+
REG_FIELD(0x7a, 0, 2), /* therm_thrsh1 */
146+
REG_FIELD(0x78, 0, 2), /* therm_thrsh2 */
126147
};
127148

128149
struct qcom_flash_data {
129150
struct v4l2_flash **v4l2_flash;
130151
struct regmap_field *r_fields[REG_MAX_COUNT];
131152
struct mutex lock;
132153
enum hw_type hw_type;
154+
u32 total_ma;
133155
u8 leds_count;
134156
u8 max_channels;
135157
u8 chan_en_bits;
158+
u8 revision;
136159
};
137160

138161
struct qcom_flash_led {
@@ -143,6 +166,7 @@ struct qcom_flash_led {
143166
u32 max_timeout_ms;
144167
u32 flash_current_ma;
145168
u32 flash_timeout_ms;
169+
u32 current_in_use_ma;
146170
u8 *chan_id;
147171
u8 chan_count;
148172
bool enabled;
@@ -172,6 +196,127 @@ static int set_flash_module_en(struct qcom_flash_led *led, bool en)
172196
return rc;
173197
}
174198

199+
static int update_allowed_flash_current(struct qcom_flash_led *led, u32 *current_ma, bool strobe)
200+
{
201+
struct qcom_flash_data *flash_data = led->flash_data;
202+
u32 therm_ma, avail_ma, thrsh[3], min_thrsh, sts;
203+
int rc = 0;
204+
205+
mutex_lock(&flash_data->lock);
206+
/*
207+
* Put previously allocated current into allowed budget in either of these two cases:
208+
* 1) LED is disabled;
209+
* 2) LED is enabled repeatedly
210+
*/
211+
if (!strobe || led->current_in_use_ma != 0) {
212+
if (flash_data->total_ma >= led->current_in_use_ma)
213+
flash_data->total_ma -= led->current_in_use_ma;
214+
else
215+
flash_data->total_ma = 0;
216+
217+
led->current_in_use_ma = 0;
218+
if (!strobe)
219+
goto unlock;
220+
}
221+
222+
/*
223+
* Cache the default thermal threshold settings, and set them to the lowest levels before
224+
* reading over-temp real time status. If over-temp has been triggered at the lowest
225+
* threshold, it's very likely that it would be triggered at a higher (default) threshold
226+
* when more flash current is requested. Prevent device from triggering over-temp condition
227+
* by limiting the flash current for the new request.
228+
*/
229+
rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH1], &thrsh[0]);
230+
if (rc < 0)
231+
goto unlock;
232+
233+
rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH2], &thrsh[1]);
234+
if (rc < 0)
235+
goto unlock;
236+
237+
if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
238+
rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH3], &thrsh[2]);
239+
if (rc < 0)
240+
goto unlock;
241+
}
242+
243+
min_thrsh = OTST_3CH_MIN_VAL;
244+
if (flash_data->hw_type == QCOM_MVFLASH_4CH)
245+
min_thrsh = (flash_data->revision == FLASH_4CH_REVISION_V0P1) ?
246+
OTST1_4CH_V0P1_MIN_VAL : OTST1_4CH_MIN_VAL;
247+
248+
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], min_thrsh);
249+
if (rc < 0)
250+
goto unlock;
251+
252+
if (flash_data->hw_type == QCOM_MVFLASH_4CH)
253+
min_thrsh = OTST2_4CH_MIN_VAL;
254+
255+
/*
256+
* The default thermal threshold settings have been updated hence
257+
* restore them if any fault happens starting from here.
258+
*/
259+
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], min_thrsh);
260+
if (rc < 0)
261+
goto restore;
262+
263+
if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
264+
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], min_thrsh);
265+
if (rc < 0)
266+
goto restore;
267+
}
268+
269+
/* Read thermal level status to get corresponding derating flash current */
270+
rc = regmap_field_read(flash_data->r_fields[REG_STATUS2], &sts);
271+
if (rc)
272+
goto restore;
273+
274+
therm_ma = FLASH_TOTAL_CURRENT_MAX_UA / 1000;
275+
if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
276+
if (sts & FLASH_STS_3CH_OTST3)
277+
therm_ma = OTST3_MAX_CURRENT_MA;
278+
else if (sts & FLASH_STS_3CH_OTST2)
279+
therm_ma = OTST2_MAX_CURRENT_MA;
280+
else if (sts & FLASH_STS_3CH_OTST1)
281+
therm_ma = OTST1_MAX_CURRENT_MA;
282+
} else {
283+
if (sts & FLASH_STS_4CH_OTST2)
284+
therm_ma = OTST2_MAX_CURRENT_MA;
285+
else if (sts & FLASH_STS_4CH_OTST1)
286+
therm_ma = OTST1_MAX_CURRENT_MA;
287+
}
288+
289+
/* Calculate the allowed flash current for the request */
290+
if (therm_ma <= flash_data->total_ma)
291+
avail_ma = 0;
292+
else
293+
avail_ma = therm_ma - flash_data->total_ma;
294+
295+
*current_ma = min_t(u32, *current_ma, avail_ma);
296+
led->current_in_use_ma = *current_ma;
297+
flash_data->total_ma += led->current_in_use_ma;
298+
299+
dev_dbg(led->flash.led_cdev.dev, "allowed flash current: %dmA, total current: %dmA\n",
300+
led->current_in_use_ma, flash_data->total_ma);
301+
302+
restore:
303+
/* Restore to default thermal threshold settings */
304+
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], thrsh[0]);
305+
if (rc < 0)
306+
goto unlock;
307+
308+
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], thrsh[1]);
309+
if (rc < 0)
310+
goto unlock;
311+
312+
if (flash_data->hw_type == QCOM_MVFLASH_3CH)
313+
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], thrsh[2]);
314+
315+
unlock:
316+
mutex_unlock(&flash_data->lock);
317+
return rc;
318+
}
319+
175320
static int set_flash_current(struct qcom_flash_led *led, u32 current_ma, enum led_mode mode)
176321
{
177322
struct qcom_flash_data *flash_data = led->flash_data;
@@ -313,6 +458,10 @@ static int qcom_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool stat
313458
if (rc)
314459
return rc;
315460

461+
rc = update_allowed_flash_current(led, &led->flash_current_ma, state);
462+
if (rc < 0)
463+
return rc;
464+
316465
rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE);
317466
if (rc)
318467
return rc;
@@ -429,6 +578,10 @@ static int qcom_flash_led_brightness_set(struct led_classdev *led_cdev,
429578
if (rc)
430579
return rc;
431580

581+
rc = update_allowed_flash_current(led, &current_ma, enable);
582+
if (rc < 0)
583+
return rc;
584+
432585
rc = set_flash_current(led, current_ma, TORCH_MODE);
433586
if (rc)
434587
return rc;
@@ -707,6 +860,14 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
707860
flash_data->hw_type = QCOM_MVFLASH_4CH;
708861
flash_data->max_channels = 4;
709862
regs = mvflash_4ch_regs;
863+
864+
rc = regmap_read(regmap, reg_base + FLASH_REVISION_REG, &val);
865+
if (rc < 0) {
866+
dev_err(dev, "Failed to read flash LED module revision, rc=%d\n", rc);
867+
return rc;
868+
}
869+
870+
flash_data->revision = val;
710871
} else {
711872
dev_err(dev, "flash LED subtype %#x is not yet supported\n", val);
712873
return -ENODEV;

0 commit comments

Comments
 (0)