Re: [PATCH v2] leds: flash: leds-qcom-flash: limit LED current based on thermal condition

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 13 May 2024, Fenglin Wu via B4 Relay wrote:

> From: Fenglin Wu <quic_fenglinw@xxxxxxxxxxx>
> 
> 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@xxxxxxxxxxx>
> ---
> Changes in v2:
> - Update thermal threshold level 2 register definition for mvflash_4ch_regs.
>     Mvflash_4ch module thermal threshold level 2 configuration register
>     offset is 0x78, not succeeding from thermal threshold level 1 register 0x7a.
>     Hence it is not appropriate to use REG_FIELD_ID to define thermal threshold
>     register fileds like mvflash_3ch. Update to use REG_FIELD instead.
> 
> - Link to v1: https://lore.kernel.org/r/20240509-qcom_flash_thermal_derating-v1-1-1d5e68e5d71c@xxxxxxxxxxx
> ---
>  drivers/leds/flash/leds-qcom-flash.c | 156 ++++++++++++++++++++++++++++++++++-
>  1 file changed, 155 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/leds/flash/leds-qcom-flash.c b/drivers/leds/flash/leds-qcom-flash.c
> index 7c99a3039171..d7c03c25941a 100644
> --- a/drivers/leds/flash/leds-qcom-flash.c
> +++ b/drivers/leds/flash/leds-qcom-flash.c
> @@ -1,6 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
>   */
>  
>  #include <linux/bitfield.h>
> @@ -14,6 +14,9 @@
>  #include <media/v4l2-flash-led-class.h>
>  
>  /* registers definitions */
> +#define FLASH_REVISION_REG		0x00
> +#define FLASH_4CH_REVISION_V0P1		0x01
> +
>  #define FLASH_TYPE_REG			0x04
>  #define FLASH_TYPE_VAL			0x18
>  
> @@ -73,6 +76,16 @@
>  
>  #define UA_PER_MA			1000
>  
> +/* thermal threshold constants */
> +#define OTST_3CH_MIN_VAL		3
> +#define OTST1_4CH_MIN_VAL		0
> +#define OTST1_4CH_V0P1_MIN_VAL		3
> +#define OTST2_4CH_MIN_VAL		0
> +
> +#define OTST1_MAX_CURRENT_MA		1000
> +#define OTST2_MAX_CURRENT_MA		500
> +#define OTST3_MAX_CURRENT_MA		200
> +
>  enum hw_type {
>  	QCOM_MVFLASH_3CH,
>  	QCOM_MVFLASH_4CH,
> @@ -98,6 +111,9 @@ enum {
>  	REG_IRESOLUTION,
>  	REG_CHAN_STROBE,
>  	REG_CHAN_EN,
> +	REG_THERM_THRSH1,
> +	REG_THERM_THRSH2,
> +	REG_THERM_THRSH3,
>  	REG_MAX_COUNT,
>  };
>  
> @@ -111,6 +127,9 @@ static struct reg_field mvflash_3ch_regs[REG_MAX_COUNT] = {
>  	REG_FIELD(0x47, 0, 5),                  /* iresolution	*/
>  	REG_FIELD_ID(0x49, 0, 2, 3, 1),         /* chan_strobe	*/
>  	REG_FIELD(0x4c, 0, 2),                  /* chan_en	*/
> +	REG_FIELD(0x56, 0, 2),			/* therm_thrsh1 */
> +	REG_FIELD(0x57, 0, 2),			/* therm_thrsh2 */
> +	REG_FIELD(0x58, 0, 2),			/* therm_thrsh3 */
>  };
>  
>  static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
> @@ -123,6 +142,8 @@ static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
>  	REG_FIELD(0x49, 0, 3),			/* iresolution	*/
>  	REG_FIELD_ID(0x4a, 0, 6, 4, 1),		/* chan_strobe	*/
>  	REG_FIELD(0x4e, 0, 3),			/* chan_en	*/
> +	REG_FIELD(0x7a, 0, 2),			/* therm_thrsh1 */
> +	REG_FIELD(0x78, 0, 2),			/* therm_thrsh2 */
>  };
>  
>  struct qcom_flash_data {
> @@ -130,9 +151,11 @@ struct qcom_flash_data {
>  	struct regmap_field     *r_fields[REG_MAX_COUNT];
>  	struct mutex		lock;
>  	enum hw_type		hw_type;
> +	u32			total_ma;
>  	u8			leds_count;
>  	u8			max_channels;
>  	u8			chan_en_bits;
> +	u8			revision;
>  };
>  
>  struct qcom_flash_led {
> @@ -143,6 +166,7 @@ struct qcom_flash_led {
>  	u32				max_timeout_ms;
>  	u32				flash_current_ma;
>  	u32				flash_timeout_ms;
> +	u32				current_in_use_ma;
>  	u8				*chan_id;
>  	u8				chan_count;
>  	bool				enabled;
> @@ -172,6 +196,121 @@ static int set_flash_module_en(struct qcom_flash_led *led, bool en)
>  	return rc;
>  }
>  
> +static int update_allowed_flash_current(struct qcom_flash_led *led, u32 *current_ma, bool strobe)
> +{
> +	struct qcom_flash_data *flash_data = led->flash_data;
> +	u32 therm_ma, avail_ma, thrsh[3], min_thrsh, sts;
> +	int rc;
> +
> +	mutex_lock(&flash_data->lock);
> +	/*
> +	 * Put previously allocated current into allowed budget in either of these two cases:
> +	 * 1) LED is disabled;
> +	 * 2) LED is enabled repeatedly
> +	 */
> +	if (!strobe || (strobe && led->current_in_use_ma != 0)) {

Isn't the check for 'strobe' here redundant?

> +		if (flash_data->total_ma >= led->current_in_use_ma)
> +			flash_data->total_ma -= led->current_in_use_ma;
> +		else
> +			flash_data->total_ma = 0;
> +
> +		led->current_in_use_ma = 0;
> +		if (!strobe) {
> +			mutex_unlock(&flash_data->lock);
> +			return 0;

Pre-initialise 'rc = 0' during the declaration, then 'goto unlock' here.

> +		}
> +	}
> +
> +	/*
> +	 * Cache the default thermal threshold settings, and set them to the lowest levels before
> +	 * reading over-temp real time status. If over-temp has been triggered at the lowest
> +	 * threshold, it's very likely that it would be triggered at a higher (default) threshold
> +	 * when more flash current is requested. Prevent device from triggering over-temp condition
> +	 * by limiting the flash current for the new request.
> +	 */
> +	rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH1], &thrsh[0]);
> +	if (rc < 0)
> +		goto unlock;
> +
> +	rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH2], &thrsh[1]);
> +	if (rc < 0)
> +		goto unlock;
> +
> +	if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
> +		rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH3], &thrsh[2]);
> +		if (rc < 0)
> +			goto unlock;
> +	}
> +
> +	min_thrsh = OTST_3CH_MIN_VAL;
> +	if (flash_data->hw_type == QCOM_MVFLASH_4CH)
> +		min_thrsh = (flash_data->revision == FLASH_4CH_REVISION_V0P1) ?
> +			OTST1_4CH_V0P1_MIN_VAL : OTST1_4CH_MIN_VAL;

Bunching up code decreases readability.

'\n'

> +	rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], min_thrsh);
> +	if (rc < 0)
> +		goto unlock;
> +
> +	if (flash_data->hw_type == QCOM_MVFLASH_4CH)
> +		min_thrsh = OTST2_4CH_MIN_VAL;

'\n'

> +	rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], min_thrsh);
> +	if (rc < 0)
> +		goto restore;

Maybe detail why you're jumping ahead here.

> +	if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
> +		rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], min_thrsh);
> +		if (rc < 0)
> +			goto restore;
> +	}
> +
> +	/* read thermal level status to get corresponding derating flash current */

Sentences start with upper case chars.

> +	rc = regmap_field_read(flash_data->r_fields[REG_STATUS2], &sts);
> +	if (rc)
> +		goto restore;
> +
> +	therm_ma = FLASH_TOTAL_CURRENT_MAX_UA / 1000;
> +	if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
> +		if (sts & FLASH_STS_3CH_OTST3)
> +			therm_ma = OTST3_MAX_CURRENT_MA;
> +		else if (sts & FLASH_STS_3CH_OTST2)
> +			therm_ma = OTST2_MAX_CURRENT_MA;
> +		else if (sts & FLASH_STS_3CH_OTST1)
> +			therm_ma = OTST1_MAX_CURRENT_MA;
> +	} else {
> +		if (sts & FLASH_STS_4CH_OTST2)
> +			therm_ma = OTST2_MAX_CURRENT_MA;
> +		else if (sts & FLASH_STS_4CH_OTST1)
> +			therm_ma = OTST1_MAX_CURRENT_MA;
> +	}
> +
> +	/* calculate the allowed flash current for the request */

As above and throughout.

> +	if (therm_ma <= flash_data->total_ma)
> +		avail_ma = 0;
> +	else
> +		avail_ma = therm_ma - flash_data->total_ma;

'\n'

> +	*current_ma = min_t(u32, *current_ma, avail_ma);
> +	led->current_in_use_ma = *current_ma;
> +	flash_data->total_ma += led->current_in_use_ma;
> +
> +	dev_dbg(led->flash.led_cdev.dev, "allowed flash current: %dmA, total current: %dmA\n",
> +					led->current_in_use_ma, flash_data->total_ma);


'\n'

> +restore:
> +	/* Restore to default thermal threshold settings */
> +	rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], thrsh[0]);
> +	if (rc < 0)
> +		goto unlock;
> +
> +	rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], thrsh[1]);
> +	if (rc < 0)
> +		goto unlock;
> +
> +	if (flash_data->hw_type == QCOM_MVFLASH_3CH)
> +		rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], thrsh[2]);
> +
> +unlock:
> +	mutex_unlock(&flash_data->lock);
> +	return rc;
> +}
> +
>  static int set_flash_current(struct qcom_flash_led *led, u32 current_ma, enum led_mode mode)
>  {
>  	struct qcom_flash_data *flash_data = led->flash_data;
> @@ -313,6 +452,10 @@ static int qcom_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool stat
>  	if (rc)
>  		return rc;
>  
> +	rc = update_allowed_flash_current(led, &led->flash_current_ma, state);
> +	if (rc < 0)
> +		return rc;
> +
>  	rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE);
>  	if (rc)
>  		return rc;
> @@ -429,6 +572,10 @@ static int qcom_flash_led_brightness_set(struct led_classdev *led_cdev,
>  	if (rc)
>  		return rc;
>  
> +	rc = update_allowed_flash_current(led, &current_ma, enable);
> +	if (rc < 0)
> +		return rc;
> +
>  	rc = set_flash_current(led, current_ma, TORCH_MODE);
>  	if (rc)
>  		return rc;
> @@ -703,6 +850,13 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
>  		flash_data->hw_type = QCOM_MVFLASH_4CH;
>  		flash_data->max_channels = 4;
>  		regs = mvflash_4ch_regs;

'\n'

> +		rc = regmap_read(regmap, reg_base + FLASH_REVISION_REG, &val);
> +		if (rc < 0) {
> +			dev_err(dev, "Read flash LED module revision failed, rc=%d\n", rc);

"Failed to read flash LED module revision"

> +			return rc;
> +		}
> +
> +		flash_data->revision = val;
>  	} else {
>  		dev_err(dev, "flash LED subtype %#x is not yet supported\n", val);
>  		return -ENODEV;
> 
> ---
> base-commit: ca66b10a11da3c445c9c0ca1184f549bbe9061f2
> change-id: 20240507-qcom_flash_thermal_derating-260b1f3c757c
> 
> Best regards,
> -- 
> Fenglin Wu <quic_fenglinw@xxxxxxxxxxx>

-- 
Lee Jones [李琼斯]




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux